使用新的 std::fs::File 创建字符串向量

Creating a vector of strings using the new std::fs::File

将我的代码从 old_io 移植到新的 std::io

let path = Path::new("src/wordslist/english.txt");
let display = path.display();
let mut file = match File::open(&path) {
    // The `desc` field of `IoError` is a string that describes the error
    Err(why) => panic!("couldn't open {}: {}", display,
                                               Error::description(&why)),
    Ok(file) => file,
};

let mut s = String::new();
match file.read_to_string(&mut s) {
    Err(why) => panic!("couldn't read {}: {}", display,
                                               Error::description(&why)),
    Ok(s) => s,
};

let words: Vec<_> = s.words().collect();

所以这可行,但需要我有一个可变字符串 s 来读取文件内容,然后使用 words().collect() 收集到一个向量中,

有没有一种方法可以使用 words() 之类的方法将文件内容读取到向量中,而无需先将其读取到可变缓冲区字符串中?我的想法是,在 collect() 调用可能稍后发生或在 words().map(something) 之后发生的情况下,这会更高效。

你的方法有问题。 .words()&str (字符串切片)上运行,需要父 String 来引用。您的示例工作正常,因为 s.words().collect() 生成的 Vec 驻留在与 s 相同的范围内,因此它不会超过源字符串。但是如果你想把它移到别处,你需要以 Vec<String> 而不是 Vec<&str> 结束,如果你担心中间缓冲区,我假设你已经想要了。

你确实有一些选择。这是我能想到的两个。

您可以遍历文件的字符并按空格拆分:

// `.peekable()` gives us `.is_empty()` for an `Iterator`
// `.chars()` yields a `Result<char, CharsError>` which needs to be dealt with
let mut chars = file.chars().map(Result::unwrap).peekable();
let mut words: Vec<String> = Vec::new();

while !chars.is_empty() {
    // This needs a type hint because it can't rely on info 
    // from the following `if` block
    let word: String = chars.take_while(|ch| !ch.is_whitespace()).collect();

    // We'll have an empty string if there's more than one 
    // whitespace character between words 
    // (more than one because the first is eaten 
    // by the last iteration of `.take_while()`)
    if !word.is_empty() {
        words.push(word);
    }
}

您可以将 File 对象包装在 std::io::BufReader 中并使用 .lines() 迭代器逐行读取它:

let mut reader = BufReader::new(file);
let mut words = Vec::new();

// `.lines()` yields `Result<String, io::Error>` so we have to handle that.
// (it will not yield an EOF error, this is for abnormal errors during reading)
for line in reader.lines().map(Result::unwrap) {
    words.extend(line.words().map(String::from_str));        
}

// Or alternately (this may not work due to lifetime errors in `flat_map()`
let words: Vec<_> = reader.lines().map(Result::unwrap)
    .flat_map(|line| line.words().map(String::from_str))
    .collect();

这两种解决方案中您更喜欢哪一种由您决定。前者可能更高效但可能不太直观。后者更容易阅读,尤其是 for 循环版本,但会分配中间缓冲区。