read_lines
単純なやり方
テキストファイルの行を読み込むのを、初心者が初めて実装した場合、 以下のようになるでしょう。
#![allow(unused)] fn main() { use std::fs::read_to_string; fn read_lines(filename: &str) -> Vec<String> { let mut result = Vec::new(); for line in read_to_string(filename).unwrap().lines() { result.push(line.to_string()) } result } }
lines()
メソッドはファイルの各行のイテレータを返すので、
インラインでマップを実行し結果を収集することもできます。
そうすると、より簡潔で読みやすい表現となります。
#![allow(unused)] fn main() { use std::fs::read_to_string; fn read_lines(filename: &str) -> Vec<String> { read_to_string(filename) .unwrap() // panic on possible file-reading errors // ファイル読み込みエラーの場合はパニックする。 .lines() // split the string into an iterator of string slices // 文字列のスライスのイテレータに分割する。 .map(String::from) // make each slice into a string // スライスを文字列に変換する。 .collect() // gather them together into a vector // ベクタにまとめる。 } }
上の例では、lines()
から返された&str
を
それぞれto_string()
とString::from
を使って、
所有権のあるString
型に変換しなければならない点に注意してください。
より効率的なやり方
ここでは、開いたFile
の所有権をBufReader
構造体に渡します。
BufReader
は内部的なバッファを使い、中間のメモリ割り当てを削減します。
read_lines
を更新して、それぞれの行に対してメモリ上に新しいString
オブジェクトを割り当てるのではなく、イテレータを返すようにします。
use std::fs::File; use std::io::{self, BufRead}; use std::path::Path; fn main() { // File hosts.txt must exist in the current path // hosts.txtファイルは現在のパスに存在しなければならない。 if let Ok(lines) = read_lines("./hosts.txt") { // Consumes the iterator, returns an (Optional) String // イテレータを消費し、Option型のStringを返す。 for line in lines { if let Ok(ip) = line { println!("{}", ip); } } } } // The output is wrapped in a Result to allow matching on errors // Returns an Iterator to the Reader of the lines of the file. // 出力はResult型にラップされ、エラーをマッチできるようになる。 // ファイルの各行のReaderへのイテレータを返す。 fn read_lines<P>(filename: P) -> io::Result<io::Lines<io::BufReader<File>>> where P: AsRef<Path>, { let file = File::open(filename)?; Ok(io::BufReader::new(file).lines()) }
このプログラムを実行すると、単に各行をプリントします。
$ echo -e "127.0.0.1\n192.168.0.1\n" > hosts.txt
$ rustc read_lines.rs && ./read_lines
127.0.0.1
192.168.0.1
File::open
はジェネリックなAsRef<Path>
を引数にとるので、
ジェネリックなread_lines
メソッドも、where
キーワードを使って、同じジェネリックな制約を持たせています。
この処理は、ファイルの中身全てをメモリ上のString
にするよりも効率的です。
メモリ上にString
を作ると、より大きなファイルを取り扱う際に、パフォーマンスの問題につながります。