我正在try 创建一个迭代器,用于从文件中读取大小为chunk_size的块.我有以下解决方案:

fn read_chunks<R: std::io::Read>(file: &mut R, chunk_size: usize) {
    let file_bytes: Vec<u8> = file.bytes().flatten().collect();
    let chunk_iter = file_bytes.chunks(chunk_size);
    // rest of code omitted ...
}

但是,它对于大文件来说太慢了.我注意到以下几点要快得多:

fn read_chunks<R: std::io::Read>(file: &mut R, chunk_size: usize) {
    loop {
        let mut buf = vec![0u8; chunk_size]
        match file.read_exact(&mut buf) {
            Ok(()) => {
                // code for handling each chunk omitted
            }
            Err(e) if e.kind() == std::io::error::ErrorKind::UnexpectedEoF => {
                break;
            }
        }
    }
}

但是如何将其作为迭代器返回呢?我想让它像chunk_iter一样,我可以将它与其他迭代器结合使用.

直觉上,我想写这样的东西(类似于python中的生成器):

fn read_chunks_iter<'a, R: std::io::Read>(file &mut R, chunk_size: usize)
    -> Iterator<Item=&'a [u8]>
 {
    loop {
        let mut buf = vec![0u8; chunk_size]
        match file.read_exact(&mut buf) {
            Ok(()) => {
                yield buf;
            }
            Err(e) if e.kind() == std::io::error::ErrorKind::UnexpectedEoF => {
                break;
            }
        }
    }
}

推荐答案

像这样一个简单的迭代器并不太难,因为循环的每次迭代都会生成单个项.我们只需要为它创建一个类型,并将循环的内容粘贴到Iterator::next中.

use std::io::{self, Read, ErrorKind};

pub struct ToChunks<R> {
    reader: R,
    chunk_size: usize,
}

impl<R: Read> Iterator for ToChunks<R> {
    type Item = io::Result<Vec<u8>>;
    
    fn next(&mut self) -> Option<Self::Item> {
        let mut buffer = vec![0u8; self.chunk_size];
        match self.reader.read_exact(&mut buffer) {
            Ok(()) => return Some(Ok(buffer)),
            Err(e) if e.kind() == ErrorKind::UnexpectedEof => None,
            Err(e) => Some(Err(e)),
        }
    }
}

在那之后,我们可以组合一个简单的特征,让它更容易打电话,我们就准备好了.


pub trait IterChunks {
    type Output;
    
    fn iter_chunks(self, len: usize) -> Self::Output;
}

impl<R: Read> IterChunks for R {
    type Output = ToChunks<R>;
    
    fn iter_chunks(self, len: usize) -> Self::Output {
        ToChunks {
            reader: self,
            chunk_size: len,
        }
    }
}

现在它应该像拨打iter_chunks(len)一样简单.

let file = BufReader::new(File::open("large_file.txt")?);

for chunk in file.iter_chunks(1024) {
    println!("{:?}", chunk);
}

Rust相关问答推荐

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

如何使用字符串迭代器执行查找?

无法定义名为&new&的关联函数,该函数的第一个参数不是self

`RwLockWriteGuard_,T`不实现T实现的特征

如何返回 struct 体中向量的切片

通过写入 std::io::stdout() 输出不可见

仅在使用 &mut 或线程时borrow 的数据在闭包之外转义?

Rust 文件未编译到 dll 中

为什么这个闭包没有实现Fn?

分配给下划线模式时会发生什么?

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

如何在 Rust 中将 bson::Bson 转换为 Vec

将 Futures 的生命周期特征绑定到 fn 参数

发生移动是因为 `data` 的类型为 `Vec`,它没有实现 `Copy` 特性

为什么基于 clap::Parser 读取的大量数字进行计算比硬编码该数字时慢?

如何为枚举中的单个或多个值返回迭代器

如何从 many0 传播 Nom 失败上下文?

为什么这里需要类型注解?

为什么对原语的引用没有隐式取消引用

翻转布尔值的最快方法