rust

Rustでcharを統一的な方法で読み込むchar_streamというクレートを作ってみた

はじめに

ストレス解消にRustでプログラミング言語でも作ってみようと思ったのだが、文字列と標準入力とファイルから統一的な方法でcharを読み込むのが面倒くさく感じた。

仕方がないので、char_streamというクレートをでっち上げてみた。

使い方

初期設定

Cargo.tomlファイルに次の行を追加。

Cargo.toml
[dependencies]
char_stream = "*"

ざっくりとした流れ

基本的には、CharStreamのオブジェクトを作成したら、next()あるいはread_line()を呼んで1文字(あるいは1行)ずつ処理する流れ。

その他に、1文字先読みするpeek()メソッドもある。

標準入力

標準入力から読み込む場合は、from_stdinメソッドを使う。

(実行すると入力待ちになるので何か文字列を入力すること)
extern crate char_stream;

use char_stream::CharStream;

fn main() {
     let mut stream = CharStream::from_stdin();
     while let Some(ch) = stream.next() {
         println!("ch: {}", ch);
     }
}

File

Fileから読むこむときはfrom_fileメソッドを使う。

extern crate char_stream;

use std::fs;
use char_stream::CharStream;

fn main(){
    let mut f = fs::File::open("testdata.txt").unwrap();
    let mut stream = CharStream::from_file(f);
    // なんか処理
}

文字列(str)

文字列(str)のときはfromメソッド。

use char_stream::CharStream;

fn main(){
    let mut stream = CharStream::from("Hello 世界❤");
    // なんか処理
}

その他

Stringの場合はfrom_string、バイト列の場合はfrom_bytesが用意してあります。
(文字列として意味のないバイト列を食わせたときの動作保証は無し)

Iterator

CharStreamはIteratorトレイトを実装しているので、Iteratorトレイトがデフォルトで提供しているメソッドも呼べる。

例えば、

extern crate char_stream;

use char_stream::CharStream;

fn main() {
    let stream = CharStream::from("Hello 世界❤");
    if let Some(c) = stream.max() {
        println!("max char: {}", c);
    }
}

のようなコードで、(意味はないけど)文字コードが最大になる文字を求めることができる。

DoubleEndedIterator

Version0.1.1から文字列(strあるいはString)から生成したCharStreamに関してのみ、DoubleEndedIteratorトレイトへ変換可能になった。
Version0.1.5から、StdInから作成したものを除いたすべてのCharStreamから、DoubleEndedIteratorトレイトに変換可能になった。
(ただし、Fileから作成したものを変換するときには、ファイルの中身を一気にすべて読み込むので注意。この読み込みはwend_iterを呼び出した時に起きる。)

変換にはwend_iter()を使う。

これにより下記コードのように、逆順を求めることなどが可能になった。
(問題は言語処理100本ノック 2015を参考にした。)

extern crate char_stream;

use char_stream::CharStream;

fn main() {
    let input = "stressed";
    let stream = CharStream::from(input);
    let rev_stream = stream.wend_iter().rev();
    let result: String = rev_stream.collect();
    println!("'{}' reverse to '{}'", input , result);
}

Example

標準入力から1行ずつ読み込む

extern crate char_stream;

use char_stream::CharStream;

fn main(){
    let mut stream = CharStream::from_stdin();
    while let Some(line) = stream.read_line() {
        println!("line: '{}'", line);
    }
}

ファイルの中身をひっくり返す

extern crate char_stream;

use std::fs::File;
use char_stream::CharStream;

fn main(){
    let f = File::open("Cargo.toml").unwrap();
    let file_stream = CharStream::from_file(f);
    let stream = file_stream.wend_iter().rev();
    let result: String = stream.collect();
    println!("{}", result);
}