read_lines

一种简单的方法

对于初学者来说,这可能是他们第一次尝试从文件中读取行的合理方法。

#![allow(unused)]
fn main() {
use std::fs::read_to_string;

fn read_lines(filename: &str) -> Vec<String> {
    let mut result = Vec::new();

    for line in read_to_string(filename).unwrap().lines() {
        result.push(line.to_string())
    }

    result
}
}

由于方法 lines() 返回文件行上的迭代器,我们也可以内联执行映射并收集结果,从而产生更简洁、更流畅的表达式。

#![allow(unused)]
fn main() {
use std::fs::read_to_string;

fn read_lines(filename: &str) -> Vec<String> {
    read_to_string(filename) 
        .unwrap()  // panic on possible file-reading errors
        .lines()  // split the string into an iterator of string slices
        .map(String::from)  // make each slice into a string
        .collect()  // gather them together into a vector
}
}

请注意,在上面的两个示例中,我们必须使用 .to_string()String::from 分别将 lines() 返回的 &str 引用转换为拥有的类型 String

一种更有效的方法

在这里,我们将打开的 File 的所有权传递给 BufReader 结构体。 BufReader 使用内部缓冲区来减少中间分配。

我们还更新了 read_lines 以返回一个迭代器,而不是为每一行在内存中分配新的 String 对象。

use std::fs::File;
use std::io::{self, BufRead};
use std::path::Path;

fn main() {
    // File hosts.txt must exist in the current path
    if let Ok(lines) = read_lines("./hosts.txt") {
        // Consumes the iterator, returns an (Optional) String
        for line in lines.flatten() {
            println!("{}", line);
        }
    }
}

// The output is wrapped in a Result to allow matching on errors.
// Returns an Iterator to the Reader of the lines of the file.
fn read_lines<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())
}

运行此程序只会单独打印每一行。

$ echo -e "127.0.0.1\n192.168.0.1\n" > hosts.txt
$ rustc read_lines.rs && ./read_lines
127.0.0.1
192.168.0.1

(请注意,由于 File::open 需要一个泛型 AsRef<Path> 作为参数,我们使用 where 关键字定义了具有相同泛型约束的泛型 read_lines() 方法。)

此过程比在内存中创建包含文件所有内容的 String 更有效。这在处理较大文件时尤其会导致性能问题。