rust - 在 Rust 中检测没有读取 0 字节的 EOF

标签 rust io

我一直在研究一些从 Read 中读取数据的代码以 block 的形式输入(输入)并对每个 block 进行一些处理。问题是最终的 block 需要用不同的函数处理。据我所知,有几种方法可以从 Read 中检测到 EOF,但没有一种方法特别适合这种情况。我正在寻找更惯用的解决方案。

我目前的做法是维护两个缓冲区,这样如果下一次读取的字节为零,则可以保持之前的读取结果,这在这种情况下表示 EOF,因为缓冲区的长度不为零:

use std::io::{Read, Result};

const BUF_SIZE: usize = 0x1000;

fn process_stream<I: Read>(mut input: I) -> Result<()> {
    // Stores a chunk of input to be processed
    let mut buf = [0; BUF_SIZE];
    let mut prev_buf = [0; BUF_SIZE];
    let mut prev_read = input.read(&mut prev_buf)?;

    loop {
        let bytes_read = input.read(&mut buf)?;
        if bytes_read == 0 {
            break;
        }

        // Some function which processes the contents of a chunk
        process_chunk(&prev_buf[..prev_read]);

        prev_read = bytes_read;
        prev_buf.copy_from_slice(&buf[..]);
    }

    // Some function used to process the final chunk differently from all other messages
    process_final_chunk(&prev_buf[..prev_read]);
    Ok(())
}

我觉得这是一种非常丑陋的方法,我不需要在这里使用两个缓冲区。

我能想到的另一种选择是强加 Seekinput 上并使用 input.read_exact()。然后我可以检查 UnexpectedEof errorkind 确定我们已经到达输入的末尾,并向后查找以再次读取最后一个 block (这里需要再次查找和读取,因为在 UnexpectedEof错误)。但这似乎一点也不符合常理:遇到错误、向后查找并再次读取以检测我们是否已到达文件末尾是非常奇怪的。

我理想的解决方案是这样的,使用一个虚构的 input.feof() 函数,如果最后一个 input.read() 调用到达 EOF,该函数返回 true,喜欢 feof syscall in C :

fn process_stream<I: Read>(mut input: I) -> Result<()> {
    // Stores a chunk of input to be processed
    let mut buf = [0; BUF_SIZE];
    let mut bytes_read = 0;

    loop {
        bytes_read = input.read(&mut buf)?;

        if input.feof() {
            break;
        }

        process_chunk(&buf[..bytes_read]);
    }

    process_final_chunk(&buf[..bytes_read]);
    Ok(())
}

任何人都可以建议一种更惯用的实现方法吗?谢谢!

最佳答案

std::io::Readread 返回 Ok(n) 时,这不仅意味着 the buffer buf has been filled in with n bytes of data from this source. ,但它也表示索引 n(含)之后的字节保持不变。考虑到这一点,您实际上根本不需要 prev_buf,因为当 n 为 0 时,缓冲区的所有字节都将保持不变(让它们成为上次读取的那些字节)。

prog-fh 的解决方案是您想要进行的那种处理,因为它只会将完整的 block 交给 process_chunk。由于 read 可能会返回一个介于 0BUF_SIZE 之间的值,因此这是必需的。有关详细信息,请参阅以上链接的这一部分:

It is not an error if the returned value n is smaller than the buffer size, even when the reader is not at the end of the stream yet. This may happen for example because fewer bytes are actually available right now (e. g. being close to end-of-file) or because read() was interrupted by a signal.

但是,我建议您考虑一下当您从 read 中得到一个 Ok(0) 时应该发生什么,它并不代表文件永远结束。看这部分:

If n is 0, then it can indicate one of two scenarios:

  1. This reader has reached its “end of file” and will likely no longer be able to produce bytes. Note that this does not mean that the reader will always no longer be able to produce bytes.

因此,如果您要获得返回 Ok(BUF_SIZE), Ok(BUF_SIZE), 0, Ok(BUF_SIZE) 的读取序列(这完全有可能,它只是代表了一个障碍IO),您不想将最后一个 Ok(BUF_SIZE) 视为读取 block 吗?如果您永远将 Ok(0) 视为 EOF,那么这可能是一个错误。

可靠地确定什么应该被视为最后一个 block 的唯一方法是将预期的长度(以字节为单位,而不是 block 的数量)作为协议(protocol)的一部分预先发送。给定一个变量 expected_len,然后您可以通过 expected_len - expected_len % BUF_SIZE 确定最后一个 block 的起始索引,而结束索引就是 expected_len 本身。

关于rust - 在 Rust 中检测没有读取 0 字节的 EOF,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/67759287/

相关文章:

vector - 仅从选项向量中获取 `Some` 内部的惯用方法?

rust - 是否可以创建一个自定义派生来防止编译时类型之间的循环?

c - Linux 内核如何设置 PCI BAR 以便不存在地址冲突?

haskell - 在 Haskell 中,我想读取一个文件,然后写入它。我需要严格注释吗?

linux - 生成无限流

rust - 首次使用时,硬件计时器立即完成。 STM32F303VC

command-line - 给定 rust 中的绝对路径,如何上传文件?

asynchronous - 如果某些情况发生,则完成 future

c - 如何在 sync/fsync/syncfs 到可移植设备后确保数据完整性

performance - Fortran 未格式化 I/O 优化