使用新的 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
循环版本,但会分配中间缓冲区。
将我的代码从 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
循环版本,但会分配中间缓冲区。