我想读取一个文件并获取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
}
s的向量。以下功能有效,但有更简洁或惯用的方式吗?
String
有些事情对我来说似乎不太理想:
&str
,这将被丢弃。如果我只想要前N行,这将特别浪费。String
,而不是以某种方式直接从文件到每行{{1}}。如何改进?
答案 0 :(得分:13)
作为BurntSushi said,您可以只使用the lines()
iterator。但是,按原样解决你的问题:
您应该阅读Error Handling in Rust;那些unwrap()
应该变成?
s,函数的结果对于某些合理的Result<Vec<String>, E>
变为E
。在这里,我们重复使用io::Result
类型别名。
使用lines()
迭代器。您可以做的另一件事是将整个文件读入String
并返回;有a lines()
iterator for strings as well。
这个你不能做任何事情:file_contents
拥有其内容,你不能将它们分成多个拥有的String
。您唯一能做的就是借用每一行的内容,然后将其转换为新的String
。也就是说,你这样做的方式意味着你相信创建一个&str
是昂贵的;事实并非如此。它只是计算一对偏移并返回它们的字面。 &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())
}
我做了另外一项更改:filename
现在是通用P: AsRef<Path>
,因为这是File::open
想要的,所以它会接受更多类型而无需转换。
答案 1 :(得分:12)
DK.'s answer非常正确并且有很好的解释。但是,你说:
读取文件并获取字符串数组
Rust数组有一个固定的长度,在编译时已知,所以我假设你的意思是&#34; vector&#34;。我会这样写:
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
还为您提供了单独分配的String
s,而不是一个大型的。Vec<String>
)。如果你想在失败时返回Result
,你可以根据需要将实施压缩到一行:
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);
}
}