Я подозреваю, что этот вопрос не указан.В частности, неясно, почему использование API ByteRecord
недостаточно.В ящике csv байтовые записи специально существуют именно для таких случаев, как этот, где ваши CSV-данные не являются строго UTF-8, но находятся в альтернативной кодировке, такой как Windows-1252, которая совместима с ASCII.(ASCII-совместимая кодировка - это кодировка, в которой ASCII является подмножеством. Windows-1252 и UTF-8 совместимы с ASCII. UTF-16 - нет.) В приведенном выше примере кода показано, что вы используете байтовые записи, но неОбъяснить, почему этого недостаточно.
С учетом сказанного, если ваша цель состоит в том, чтобы перевести ваши данные в строковый тип данных Rust (String
/ &str
), тогда ваш вариант only это перекодировать содержимое ваших CSV-данных из Windows-1252 в UTF-8.Это необходимо, потому что строковый тип данных Rust использует UTF-8 для представления в памяти.У вас не может быть Rust String
/ &str
, который закодирован в Windows-1252, потому что Windows-1252 не является подмножеством UTF-8.
В других комментариях рекомендуется использовать ящик encoding
.Однако вместо этого я бы рекомендовал использовать encoding_rs
, если ваш сценарий использования совпадает с теми же сценариями использования, которые определены в стандарте Encoding , специально предназначенном для Интернета.К счастью, я считаю, что такое выравнивание существует.
Для того, чтобы удовлетворить ваши критерии для чтения данных CSV в потоковом режиме без предварительной загрузки всего содержимого в память, вам нужно использовать обертку вокруг ящика encoding_rs
который реализует потоковое декодирование для вас.Ящик encoding_rs_io
обеспечивает это для вас.(Он используется внутри ripgrep для быстрого декодирования потоковой передачи перед поиском в UTF-8.)
Вот пример программы, которая объединяет все вышеперечисленное с использованием Rust 2018:
use std::fs::File;
use std::process;
use encoding_rs::WINDOWS_1252;
use encoding_rs_io::DecodeReaderBytesBuilder;
fn main() {
if let Err(err) = try_main() {
eprintln!("{}", err);
process::exit(1);
}
}
fn try_main() -> csv::Result<()> {
let file = File::open("test.csv")?;
let transcoded = DecodeReaderBytesBuilder::new()
.encoding(Some(WINDOWS_1252))
.build(file);
let mut rdr = csv::ReaderBuilder::new()
.delimiter(b';')
.from_reader(transcoded);
for result in rdr.records() {
let r = result?;
println!("{:?}", r);
}
Ok(())
}
с Cargo.toml
:
[package]
name = "so53826986"
version = "0.1.0"
edition = "2018"
[dependencies]
csv = "1"
encoding_rs = "0.8.13"
encoding_rs_io = "0.1.3"
и выводом:
$ cargo run --release
Compiling so53826986 v0.1.0 (/tmp/so53826986)
Finished release [optimized] target(s) in 0.63s
Running `target/release/so53826986`
StringRecord(["Café", "au", "lait"])
В частности, если вы поменяете rdr.records()
на rdr.byte_records()
, тогдамы можем более четко увидеть, что произошло:
$ cargo run --release
Compiling so53826986 v0.1.0 (/tmp/so53826986)
Finished release [optimized] target(s) in 0.61s
Running `target/release/so53826986`
ByteRecord(["Caf\\xc3\\xa9", "au", "lait"])
А именно, ваш вход содержал Caf\xE9
, но запись байта теперь содержит Caf\xC3\xA9
.Это является результатом преобразования значения кодовой точки Windows-1252 233
(закодированного как его буквенный байт \xE9
) в U+00E9 LATIN SMALL LETTER E WITH ACUTE
, который в кодировке UTF-8 закодирован как \xC3\xA9
.