在Rust中逐个字符地读取文件

Tim*_*ean 14 io rust

在Rust中,有一种惯用的方法可以一次处理一个字符的文件吗?

这似乎与我追求的大致相同:

let mut f = io::BufReader::new(try!(fs::File::open("input.txt")));

for c in f.chars() {
    println!("Character: {}", c.unwrap());
}
Run Code Online (Sandbox Code Playgroud)

但是Read::chars从Rust v1.6.0开始仍然不稳定.

我考虑过使用Read::read_to_string,但文件可能很大,我不想把它全部读入内存.

mal*_*rbo 6

让我们比较4种方法.

1. Read::chars

您可以复制Read::chars实现,但它标记为不稳定

部分读/写错误发生的语义目前尚不清楚,可能会发生变化

所以必须要小心.无论如何,这似乎是最好的方法.

2. flat_map

flat_map替代不会编译:

use std::io::{BufRead, BufReader};
use std::fs::File;

pub fn main() {
    let mut f = BufReader::new(File::open("input.txt").expect("open failed"));

    for c in f.lines().flat_map(|l| l.expect("lines failed").chars()) {
        println!("Character: {}", c);
    }
}
Run Code Online (Sandbox Code Playgroud)

问题是chars从字符串借用,但l.expect("lines failed")只存在于闭包内部,因此编译器会给出错误borrowed value does not live long enough.

3.嵌套

这段代码

use std::io::{BufRead, BufReader};
use std::fs::File;

pub fn main() {
    let mut f = BufReader::new(File::open("input.txt").expect("open failed"));

    for line in f.lines() {
        for c in line.expect("lines failed").chars() {
            println!("Character: {}", c);
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

工作,但它为每一行保持分配一个字符串.此外,如果输入文件没有换行符,整个文件将加载到内存中.

4. BufRead::read_until

方法3的内存有效替代方法是使用Read::read_until,并使用单个字符串来读取每一行:

use std::io::{BufRead, BufReader};
use std::fs::File;

pub fn main() {
    let mut f = BufReader::new(File::open("input.txt").expect("open failed"));

    let mut buf = Vec::<u8>::new();
    while f.read_until(b'\n', &mut buf).expect("read_until failed") != 0 {
        // this moves the ownership of the read data to s
        // there is no allocation
        let s = String::from_utf8(buf).expect("from_utf8 failed");
        for c in s.chars() {
            println!("Character: {}", c);
        }
        // this returns the ownership of the read data to buf
        // there is no allocation
        buf = s.into_bytes();
        buf.clear();
    }
}
Run Code Online (Sandbox Code Playgroud)