读取文件并获取字符串数组

时间:2015-06-12 10:38:50

标签: rust idiomatic

我想读取一个文件并获取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}}。

如何改进?

2 个答案:

答案 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);
    }
}
  1. 与其他答案一样,使用为文件名实现AsRef的泛型类型是值得的。
  2. Result::expect可以缩短Err的恐慌情绪。
  3. BufRead::lines处理多种类型的换行符,而不仅仅是"\n"
  4. BufRead::lines还为您提供了单独分配的String s,而不是一个大型的。
  5. 没有理由收集临时变量只是为了返回它。特别没有理由重复该类型(Vec<String>)。
  6. 如果你想在失败时返回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);
        }
    }