Читать файл (не utf-8) построчно?

Можно ли читать файл построчно, если он не находится в кодировке utf-8 с std::io::File а также std::io::BufReader?

я смотрю на std::io::Lines и это вернуть Result<String>Я так волнуюсь, могу ли я реализовать свой собственный BufReader которые делают то же самое, но возвращают Vec<u8> вместо этого, или я могу использовать повторно std::io::BufReader каким-то образом?

3 ответа

Решение

Вам не нужно повторно внедрять BufReader сам он предоставляет именно тот метод, который вам нужен read_until:

fn read_until(&mut self, byte: u8, buf: &mut Vec<u8>) -> Result<usize>

Вы поставляете свой собственный Vec<u8> и содержимое файла будет добавлено до byte встречается (0x0A является LF).

Есть несколько потенциальных ошибок:

  • буфер может заканчиваться не только байтом LF, но и последовательностью LF CR,
  • это зависит от вас, чтобы очистить buf между последующими звонками.

Просто while let Ok(_) = reader.read_until(0x0A as u8, buffer) должен позволить вам прочитать ваш файл достаточно легко.

Вы можете рассмотреть возможность реализации std::io::Lines эквивалент, который преобразует кодировку в UTF-8, чтобы обеспечить хороший API, хотя это будет иметь затраты на производительность.

Вот более упрощенная версия.

Двойной проверки декодируемости ввода с помощью UTF-8 не требуется, поэтому его необходимо закодировать в Windows 1252.

      use std::path::PathBuf;
use std::fs::File;
use std::io::{self, BufRead, BufReader};

use encoding_rs::WINDOWS_1252;
use encoding_rs_io::DecodeReaderBytesBuilder;

fn main() -> io::Result<()> {
    let path = PathBuf::from("path/to/your/file.txt");
    let file = File::open(path)?;
    let reader = BufReader::new(DecodeReaderBytesBuilder::new()
                    .encoding(Some(WINDOWS_1252))
                    .build(file));
    
    for (i, rst) in reader.lines().enumerate() {
        let line_number :u32 = i+1;
        if let Ok(line) = rst {
            // 'line' is decoded String line here
        }
    }
    
    Ok(())
}

Чтобы читать файлы (utf-8 или не utf-8) построчно, я использую:

      /*
// Cargo.toml:
[dependencies]
encoding_rs = "0.8"
encoding_rs_io = "0.1.7"
...
*/

use encoding_rs::WINDOWS_1252;
use encoding_rs_io::DecodeReaderBytesBuilder;
use std::{
    fs,
    io::{Read, BufRead, BufReader, Error},
};

fn main() {
    let file_path: &str = "/tmp/file.txt";
    let buffer: Box<dyn BufRead> = read_file(file_path);

    for (index, result_vec_bytes) in buffer.split(b'\n').enumerate() {
        let line_number: usize = index + 1;
        let line_utf8: String = get_string_utf8(result_vec_bytes, line_number, file_path);
        println!("{line_utf8}");
    }
}

Такой, что:

      fn read_file(file_path: &str) -> Box<dyn BufRead> {
    let file = match fs::File::open(file_path) {
        Ok(f) => f,
        Err(why) => panic!("Problem opening the file: \"{file_path}\"\n{why:?}"),
    };
    Box::new(BufReader::new(file))
}

И

      fn get_string_utf8(result_vec_bytes: Result<Vec<u8>, std::io::Error>, line_number: usize) -> String {
    let vec_bytes: Vec<u8> = match result_vec_bytes {
        Ok(values) => values,
        Err(why) => panic!("Failed to read line nº {line_number}: {why}"),
    };

    // from_utf8() checks to ensure that the bytes are valid UTF-8
    let line_utf8: String = match std::str::from_utf8(&vec_bytes) {
        Ok(str) => str.to_string(),
        Err(_) => {
            let mut data = DecodeReaderBytesBuilder::new()
            .encoding(Some(WINDOWS_1252))
            .build(vec_bytes.as_slice());

            let mut buffer = String::new();
            let _number_of_bytes = match data.read_to_string(&mut buffer) {
                Ok(num) => num,
                Err(why) => {
                    eprintln!("Problem reading data from file in buffer!");
                    eprintln!("Line nº {line_number}");
                    eprintln!("Used encoding type: WINDOWS_1252.");
                    eprintln!("Try another encoding type!");
                    panic!("Failed to convert data to UTF-8!: {why}")
                },
            };

            buffer
        }
    };

    // remove Window new line: "\r\n"
    line_utf8.trim_end_matches('\r').to_string()
}
Другие вопросы по тегам