从标准输入读取包括 n 在内的整行,直到文件结束

我想将此 Python 代码转换为 Rust:

for line in sys.stdin:
   do something to the whole line including n

但我只能找到读取单个完整行的示例(包括 n )或不读取 n 的示例。

看起来这应该是世界上最简单的,但我一直找不到。

stack overflow Read full lines from stdin including \n until end-of-file
原文答案

答案:

作者头像

PythonRust 都提供了方便的 API 来迭代文件中的行,它们只是选择了方便与完整性的不同权衡。 Rust 选择了额外的便利性并以能够区分最后一行是否以终止符结尾为代价去除换行符。 Python 选择了相反的情况,代价是所有行解析器都必须考虑最终的 n ,并且还要考虑到它是 optional

但是 BufRead::lines() 只是一个方便的 AP;如果它不能满足您的需求,您可以随时使用较低级别的 read_line() 方法:

let mut line = String::new();
while input.read_line(&mut line)? != 0 {
    let line = std::mem::take(&mut line);
    // ...
}

如果您在多个地方使用这种代码,或者只是想要 for 循环的便利,您可以将其抽象为一个返回迭代器的实用函数,例如:

fn full_lines(mut input: impl BufRead) -> impl Iterator<Item = io::Result<String>> {
    std::iter::from_fn(move || {
        let mut vec = String::new();
        match input.read_line(&mut vec) {
            Ok(0) => None,
            Ok(_) => Some(Ok(vec)),
            Err(e) => Some(Err(e)),
        }
    })
}

然后您可以使用类似于 Python 中的 for 循环:

for line in full_lines(io::stdin().lock()) {
    let line = line?;
    // ...
}

通过额外的努力,甚至可以使 full_lines 成为任何实现 BufRead 的方法:

trait FullLines: BufRead + Sized {
    fn full_lines<'a>(self) -> Box<dyn Iterator<Item = io::Result<String>> + 'a>
    where
        Self: 'a,
    {
        Box::new(full_lines(self))
    }
}

// Provide a blanket implementation of FullLines for any T
// that implements BufRead
impl<T: BufRead> FullLines for T {}

// Usage:

use FullLines;

for line in io::stdin().lock().full_lines() {
    let line = line?;
    // ...
}