【问题标题】:Read large files line by line in Rust [duplicate]在 Rust 中逐行读取大文件 [重复]
【发布时间】:2018-02-03 13:24:46
【问题描述】:

我的 Rust 程序旨在逐行读取一个非常大(最多几 GB)的简单文本文件。问题是,这个文件太大而无法一次读取,或者将所有行都转移到Vec<String>。

在 Rust 中处理这个问题的惯用方法是什么?

【问题讨论】:

  • @Kroltan - 没那么多;根据 Google 搜索“从 rust 文件中有效读取行”,此页面现在是排名最高的条目。 (-:
  • @HuwWalters 发帖时还没有!当前读者将被定向到任何这些副本。而且我敢说 SO 帖子仍然有资格作为“示例”,所以评论仍然是最新的(-:
  • 那是我的观点;这是一个有用的页面! (虽然讽刺可能有点平淡)
  • 我不同意这个问题已经在 SO 上得到了回答,当然也不是通过存在的链接。 OP 专门为 very large 文件请求了一种方法,其中文件太大而无法一次读取或逐行读取,必须存在更有效的方法,这使得这是一个有效的问题跨度>

标签: rust


【解决方案1】:

您想使用buffered reader, BufRead,特别是函数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

【讨论】:

  • 请注意,换行符被认为是 LF 或 CR 后跟 LF。
  • 如果文件大小大于可用内存,这不一定是最有效的方法
  • @Awalias 我更新了我的答案以更清晰。如果你不明白具体的东西,请告诉我。
  • 开始学习 Rust,这非常有用且内容丰富
猜你喜欢
  • 2023-03-27
  • 1970-01-01
  • 1970-01-01
  • 2020-10-24
  • 2015-05-19
  • 2020-02-21
  • 1970-01-01
  • 2017-08-29
  • 1970-01-01
相关资源
最近更新 更多