Почему вызов BufReader :: fill_buf после вызова потребляет меньше байтов, чем я ожидал? - PullRequest
0 голосов
/ 04 октября 2018

Я пытаюсь реализовать потоковую передачу символов 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-байтовый буфер.Это нормально, я просто игнорирую это и декодирую то, что действительно до начала этого символа.

Проблема в том, что после вызова consume(98) для BufReader следующий вызов 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() вернул частично декодированную строку и позицию ошибки декодирования, такМне не нужно вызывать его дважды, когда это происходит, но в стандартной библиотеке, похоже, такой функции не существует (о которой я знаю).

1 Ответ

0 голосов
/ 04 октября 2018

Я призываю вас научиться создавать Минимальный, Полный и Проверяемый пример .Это ценный навык, который профессиональные программисты используют для лучшего понимания проблем и сосредоточения внимания на важных аспектах проблемы.Например, вы не указали фактический входной файл, поэтому очень сложно воспроизвести ваше поведение с помощью предоставленного вами кода.

После проб и ошибок я смог уменьшить вашу проблемук этому коду:

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 и фактически почти требуется .Смысл буферизованного читателя состоит в том, чтобы максимально избегать вызовов основного читателя.Эта черта не знает, сколько байтов нужно прочитать, и она не знает, что 2 байта недостаточно, и она должна выполнить еще один вызов.Если щелкнуть по-другому, представьте, что вы израсходовали только 1 байт из 100 - хотели бы вы, чтобы все 99 оставшихся байтов были скопированы в память, а затем выполнили еще одно базовое чтение?Это было бы медленнее, чем вообще не использовать BufRead!

Эта черта также не имеет каких-либо условий для перемещения оставшихся байтов в буфере в начало и последующего заполнения буфера снова.Это то, что может быть добавлено к бетону 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(())
}

См. Также:

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...