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

时间:2015-03-23 17:14:19

标签: string performance vector rust

将我的代码从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();

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

有没有办法使用像words()这样的东西读取文件的内容而不首先将它读取到可变缓冲区字符串?我的想法是,在collect()调用可能在稍后的某个时间点发生,或者在一个单词()。map(某事物)之后,这会更有效。

1 个答案:

答案 0 :(得分:2)

您的方法存在问题。 .words()&str(字符串切片)上运行,需要父String来引用。您的示例运行正常,因为Vec生成的s.words().collect()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 - 循环版本,但分配中间缓冲区。