gpt4 book ai didi

rust - 为什么在调用 consume 后调用 BufReader::fill_buf 返回的字节数比我预期的要少?

转载 作者:行者123 更新时间:2023-11-29 08:20:24 27 4
gpt4 key购买 nike

我正在尝试实现文件中的 UTF-8 字符流。这就是我到目前为止所得到的,请暂时原谅丑陋的代码。

use std::fs::File;
use std::io;
use std::io::BufRead;
use std::str;

fn main() -> io::Result<()> {
let mut reader = io::BufReader::with_capacity(100, File::open("utf8test.txt")?);
loop {
let mut consumed = 0;
{
let buf = reader.fill_buf()?;
println!("buf len: {}", buf.len());
match str::from_utf8(&buf) {
Ok(s) => {
println!("====\n{}", s);
consumed = s.len();
}
Err(err) => {
if err.valid_up_to() == 0 {
println!("1. utf8 decoding failed!");
} else {
match str::from_utf8(&buf[..err.valid_up_to()]) {
Ok(s) => {
println!("====\n{}", s);
consumed = s.len();
}
_ => println!("2. utf8 decoding failed!"),
}
}
}
}
}
if consumed == 0 {
break;
}
reader.consume(consumed);
println!("consumed {} bytes", consumed);
}
Ok(())
}

我有一个在偏移量 98 处有一个多字节字符的测试文件,它无法解码,因为它不完全适合我的(任意大小的)100 字节缓冲区。没关系,我只是忽略它并解码直到该字符开头的有效内容。

问题是在 BufReader 上调用 consume(98) 之后,下一次调用 fill_buf() 只返回 2 个字节。 .. 它似乎没有费心将更多字节读入缓冲区。我不明白为什么。也许我误解了文档。

这是示例输出:

buf len: 100
====
UTF-8 encoded sample plain-text file
‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾
consumed 98 bytes
buf len: 2
1. utf8 decoding failed!

如果 from_utf8() 返回部分解码的字符串和解码错误的位置就好了,这样我就不必在发生这种情况时调用它两次,但是没有标准库中似乎有这样一个函数(据我所知)。

最佳答案

我鼓励您学习如何制作 Minimal, Complete, and Verifiable example .这是专业程序员用来更好地理解问题并将注意力集中在问题的重要方面的宝贵技能。例如,您没有提供实际的输入文件,因此任何人都很难使用您提供的代码重现您的行为。

经过反复试验,我能够将您的问题简化为以下代码:

use std::io::{self, BufRead};

fn main() -> io::Result<()> {
let mut reader = io::BufReader::with_capacity(100, io::repeat(b'a'));

let a = reader.fill_buf()?.len();
reader.consume(98);
let b = reader.fill_buf()?.len();

println!("{}, {}", a, b); // 100, 2

Ok(())
}

不幸的是,对于您的情况,BufRead 的契约(Contract)允许这种行为事实上几乎是必需的。缓冲读取器的要点是尽可能避免调用底层读取器。 trait 不知道你需要读取多少字节,也不知道 2 个字节不够,它应该执行另一个调用。反过来,假设您只消耗了 100 个字节中的 1 个字节——您是否希望将所有剩余的 99 个字节复制到内存中,然后执行另一次底层读取?这比一开始不使用 BufRead 要慢!

trait 也没有任何规定将缓冲区中的剩余字节移动到开头,然后再次填充缓冲区。这似乎可以添加到具体的 BufReader 中。 ,因此您可能希望提供拉取请求以添加它。

现在,我建议使用 Read::read_exact在缓冲区的末尾:

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

fn main() -> io::Result<()> {
let mut reader = io::BufReader::with_capacity(100, io::repeat(b'a'));

let a = reader.fill_buf()?.len();
reader.consume(98);

let mut leftover = [0u8; 4]; // a single UTF-8 character is at most 4 bytes
// Assume we know we need 3 bytes based on domain knowledge
reader.read_exact(&mut leftover[..3])?;

let b = reader.fill_buf()?.len();

println!("{}, {}", a, b); // 100, 99

Ok(())
}

另见:

关于rust - 为什么在调用 consume 后调用 BufReader::fill_buf 返回的字节数比我预期的要少?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/52650080/

27 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com