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

Posted

技术标签:

【中文标题】读取文件并获取字符串数组【英文标题】:Read a file and get an array of strings 【发布时间】:2015-08-28 08:14:26 【问题描述】:

我想读取一个文件并取回Strings 的向量。以下函数有效,但有没有更简洁或惯用的方式?

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

一些对我来说似乎不是最理想的事情:

两个单独的错误检查用于读取文件。 将整个文件读取到String,该文件将被丢弃。如果我只想要前 N 行,这将特别浪费。 每行创建一个&amp;str,这将被丢弃,而不是以某种方式直接从文件到每行一个String

如何改进?

【问题讨论】:

使用lines()迭代器:doc.rust-lang.org/std/io/trait.BufRead.html#method.lines 我不明白为什么这个问题被否决了。如果它被认为过于主观,我建议删除idiomatic 标签,因为它准确地描述了我要问的事情。 【参考方案1】:

作为BurntSushi said,您可以只使用the lines() iterator。但是,按原样解决您的问题:

你应该读一下Error Handling in Rust;那些unwrap()s 应该变成?s,对于一些合理的E,函数的结果变成Result&lt;Vec&lt;String&gt;, E&gt;。在这里,我们重用了io::Result 类型别名。

使用lines() 迭代器。您可以做的另一件事是将整个文件读入String并返回;有a lines() iterator for strings as well。

这个你无能为力:file_contents 拥有它的内容,你不能将它们分成多个拥有的Strings。您唯一能做的就是借用每一行的内容,然后将其转换为新的String。也就是说,您的表述方式暗示您认为创建&amp;str 是昂贵的;不是。 字面意思只是计算一对偏移量并返回它们。 &amp;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&lt;Path&gt;,因为这是File::open 想要的,所以它可以接受更多类型而无需转换。

【讨论】:

整洁!但是返回 Result 是否意味着如果文件不可读,这将是调用者的问题?也许没有办法避免这种情况,同时又懒惰地阅读文件? @NathanLong 使用unwrappanic! 意味着如果文件不可读,则整个线程爆炸,调用者在没有警告的情况下死亡。如果调用者不关心这一点,他们可以在结果上调用unwrap 并获得相同的爆炸行为。 或者他们实际上可以决定如何处理错误。无论哪种方式,它都不会影响读取文件:两者都会导致函数以一种或另一种方式停止执行。 你能添加一个调用lines_from_file的例子吗?【参考方案2】:

DK.'s answer 非常正确并且有很好的解释。但是,您说:

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

Rust 数组有一个固定的长度,在编译时就知道了,所以我假设你的意思是“向量”。我会这样写:

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 还为您提供单独分配的 Strings,而不是一大堆。 没有理由收集到临时变量只是为了返回它。尤其没有理由重复该类型 (Vec&lt;String&gt;)。

如果您想在失败时返回 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);
    

【讨论】:

你的'down to one line'函数似乎将整个文本作为1行返回给我,你的第一个工作。 @Blankman 我认为问题在于您没有处理从lines_from_file 返回的ResultOptionResult 实现 IntoIterator,所以这可能会欺骗你。

以上是关于读取文件并获取字符串数组的主要内容,如果未能解决你的问题,请参考以下文章

libdbus 读取数组类型结果

读入字符并创建数组c ++

动态读取文本文件并放入 C 中的指针字符数组

c语言中从文件中按行读取字符串,并进行相应操作。

C语言fgets()函数(以指定长度读取文件中的字符,并存入字符数组变量中)

VB如何读取一个图片,保存到字节数组中