Читать файл (не 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()
}