我的Rust程序旨在逐行读取一个非常大(高达几GB)的简单文本文件.问题是,这个文件太大,无法一次读取,或者无法将所有行传输到Vec<String>.

用什么惯用方法来处理这个问题?

推荐答案

具体来说,使用BufReader.lines()函数:

use std::fs::File;
use std::io::{self, prelude::*, BufReader};

fn main() -> io::Result<()> {
    let file = File::open("foo.txt")?;
    let reader = BufReader::new(file);

    for line in reader.lines() {
        println!("{}", line?);
    }

    Ok(())
}

请注意,正如文档中所述,您返回了换行符.


如果不想为每一行分配一个字符串,下面是一个重用同一缓冲区的示例:

fn main() -> std::io::Result<()> {
    let mut reader = my_reader::BufReader::open("Cargo.toml")?;
    let mut buffer = String::new();

    while let Some(line) = reader.read_line(&mut buffer) {
        println!("{}", line?.trim());
    }

    Ok(())
}

mod my_reader {
    use std::{
        fs::File,
        io::{self, prelude::*},
    };

    pub struct BufReader {
        reader: io::BufReader<File>,
    }

    impl BufReader {
        pub fn open(path: impl AsRef<std::path::Path>) -> io::Result<Self> {
            let file = File::open(path)?;
            let reader = io::BufReader::new(file);

            Ok(Self { reader })
        }

        pub fn read_line<'buf>(
            &mut self,
            buffer: &'buf mut String,
        ) -> Option<io::Result<&'buf mut String>> {
            buffer.clear();

            self.reader
                .read_line(buffer)
                .map(|u| if u == 0 { None } else { Some(buffer) })
                .transpose()
        }
    }
}

Playground

或者,如果你更喜欢标准迭代器,你可以使用这个Rc技巧,我无耻地用了from Reddit:

fn main() -> std::io::Result<()> {
    for line in my_reader::BufReader::open("Cargo.toml")? {
        println!("{}", line?.trim());
    }

    Ok(())
}

mod my_reader {
    use std::{
        fs::File,
        io::{self, prelude::*},
        rc::Rc,
    };

    pub struct BufReader {
        reader: io::BufReader<File>,
        buf: Rc<String>,
    }
    
    fn new_buf() -> Rc<String> {
        Rc::new(String::with_capacity(1024)) // Tweakable capacity
    }

    impl BufReader {
        pub fn open(path: impl AsRef<std::path::Path>) -> io::Result<Self> {
            let file = File::open(path)?;
            let reader = io::BufReader::new(file);
            let buf = new_buf();

            Ok(Self { reader, buf })
        }
    }

    impl Iterator for BufReader {
        type Item = io::Result<Rc<String>>;

        fn next(&mut self) -> Option<Self::Item> {
            let buf = match Rc::get_mut(&mut self.buf) {
                Some(buf) => {
                    buf.clear();
                    buf
                }
                None => {
                    self.buf = new_buf();
                    Rc::make_mut(&mut self.buf)
                }
            };

            self.reader
                .read_line(buf)
                .map(|u| if u == 0 { None } else { Some(Rc::clone(&self.buf)) })
                .transpose()
        }
    }
}

Playground

Rust相关问答推荐

为什么我需要在这个代码示例中使用&

当Option为None时,Option数组是否占用Rust中的内存?

修改切片/引用数组

为什么允许我们将可变引用转换为不可变引用?

使用Box优化可选的已知长度数组的内存分配

如何点击()迭代器?

不能在Rust中使用OpenGL绘制三角形

我如何使用AWS SDK for Rust获取我承担的角色的凭据?

在Rust内联程序集中使用字符串常量

Rust ndarray:如何从索引中 Select 数组的行

Windows 上 ndarray-linalg 与 mkl-stats 的链接时间错误

如何对一个特征的两个实现进行单元测试?

为什么实现特征的对象期望比具体对象有更长的生命周期?

闭包返回类型的生命周期规范

Rust 为什么被视为borrow ?

Rust: 目标成员属于哪个"目标家族"的列表是否存在?

没有明确地说return会产生错误:match arms have incompatible types

Rust 中的自动取消引用是如何工作的?

Rust Serde 为 Option:: 创建反序列化器

当用作函数参数时,不强制执行与绑定的关联类型