ファイルを読み取ってString
sのベクターを取得したい。次の関数は機能しますが、より簡潔または慣用的な方法はありますか?
use std::fs::File;
use std::io::Read;
fn lines_from_file(filename: &str) -> Vec<String> {
let mut file = match File::open(filename) {
Ok(file) => file,
Err(_) => panic!("no such file"),
};
let mut file_contents = String::new();
file.read_to_string(&mut file_contents)
.ok()
.expect("failed to read!");
let lines: Vec<String> = file_contents.split("\n")
.map(|s: &str| s.to_string())
.collect();
lines
}
私にとって次善のように思われるいくつかのこと:
String
に読み取ると、破棄されます。最初のN行だけが必要な場合、これは特に無駄になります。&str
1行あたり。何とかしてファイルからString
1行あたりに直接移動するのではなく、破棄されます。これをどのように改善できますか?
DK。の答え は非常に正確であり、すばらしい説明があります。ただし、次のように述べています。
ファイルを読み取り、文字列の配列を取得する
Rust配列は固定長であり、コンパイル時に認識されるため、「ベクトル」を意味していると思います。私はそれを次のように書きます:
use std::{
fs::File,
io::{prelude::*, BufReader},
path::Path,
};
fn lines_from_file(filename: impl AsRef<Path>) -> Vec<String> {
let file = File::open(filename).expect("no such file");
let buf = BufReader::new(file);
buf.lines()
.map(|l| l.expect("Could not parse line"))
.collect()
}
// ---
fn main() {
let lines = lines_from_file("/etc/hosts");
for line in lines {
println!("{:?}", line);
}
}
AsRef
を実装するジェネリック型を使用することは価値があります。Result::expect
Err
のパニックを短縮します。BufRead::lines
は、"\n"
だけでなく、複数の種類の改行を処理します。BufRead::lines
は、1つの大きなグロブの代わりに、個別に割り当てられたString
sも提供します。Vec<String>
)。失敗時にResult
を返したい場合、必要に応じて実装を1行に縮小できます。
use std::{
fs::File,
io::{self, BufRead, BufReader},
path::Path,
};
fn lines_from_file(filename: impl AsRef<Path>) -> io::Result<Vec<String>> {
BufReader::new(File::open(filename)?).lines().collect()
}
// ---
fn main() {
let lines = lines_from_file("/etc/hosts").expect("Could not load lines");
for line in lines {
println!("{:?}", line);
}
}
BurntSushiが言った のように、あなたは単に the lines()
iterator を使用できます。ただし、質問に現状のまま対処するには:
おそらく Rustでのエラー処理 ;を読んでください。これらのunwrap()
sは_?
_ sに変換する必要があります。関数の結果は、合理的なE
の場合は_Result<Vec<String>, E>
_になります。ここでは、_io::Result
_タイプのエイリアスを再利用しています。
lines()
イテレータを使用します。他にできることは、ファイル全体をString
に読み込み、それを返すことです; a lines()
文字列のイテレータ があります。
これはあなたが何もすることはできません:_file_contents
_はその内容を所有し、それらを複数の所有されたString
sに分割することはできません。あなたができる唯一のことは、各行の内容を借りて、それを新しいString
に変換することです。とはいえ、これを表現する方法は、_&str
_の作成にはコストがかかると考えていることを意味します。そうではありません。これはliterallyオフセットのペアを計算してそれらを返すだけです。 _&str
_スライスは、実質的に_(*const u8, usize)
_と同等です。
基本的に同じことを行う修正バージョンは次のとおりです。
_use std::fs::File;
use std::io::{self, BufRead};
use std::path::Path;
fn lines_from_file<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())
}
_
私が行ったもう1つの変更:filename
が一般的な_P: AsRef<Path>
_になりました。これは _File::open
_ が必要とするものであり、変換を必要とせずにより多くの型を受け入れるためです。