如何使用Rust加载格式不规则的CSV文件?
解决CSV文件格式不一致导致的加载错误
需要加载的test.csv文件前两行是元数据(包含年份、学期等信息),格式为5个字段(含末尾空字段),但第三行及之后的数据行仅4个字段,这种格式差异导致csv库抛出UnequalLengths错误。
test.csv内容
[YEAR],2022,[Q],1, [TEST],mid-term,[GRADE],3, FirstName,LastName,Score, AA,aaa,97, BB,bbbb,15, CC,cccc,66, DD,ddd,73, EE,eeeee,42, FF,fffff,52, GG,ggg,64, HH,h,86, II,iii,88, JJ,jjjj,72,
错误信息
StringRecord(["[YEAR]", "2022", "[Q]", "1", ""]) StringRecord(["[TEST]", "mid-term", "[GRADE]", "3", ""]) Error: Error(UnequalLengths { pos: Some(Position { byte: 47, line: 2, record: 2 }), expected_len: 5, len: 4 }) error: process didn't exit successfully: `target\debug\read_csv.exe` (exit code: 1)
当前代码(main.rs)
use csv::Error; use csv::ReaderBuilder; use encoding_rs; use std::fs; fn main() -> Result<(), Error> { let path = "./test.csv"; let file = fs::read(path).unwrap(); let (res, _, _) = encoding_rs::SHIFT_JIS.decode(&file); let mut reader = ReaderBuilder::new() .has_headers(false) .from_reader(res.as_bytes()); for result in reader.records() { let record = result?; println!("{:?}", record) } Ok(()) }
版本信息
cargo = "1.62.0" rustc = "1.62.0" csv = "1.1.6" encoding_rs = "0.8.31"
解决方案
有两种处理方式,可根据实际需求选择:
方式1:分开处理元数据与数据行
如果只需要读取第三行及之后的业务数据,或者需要单独解析前两行元数据,可以先手动读取并处理前两行,再读取剩余数据行:
use csv::Error; use csv::ReaderBuilder; use encoding_rs; use std::fs; fn main() -> Result<(), Error> { let path = "./test.csv"; let file = fs::read(path).unwrap(); let (res, _, _) = encoding_rs::SHIFT_JIS.decode(&file); let mut reader = ReaderBuilder::new() .has_headers(false) .from_reader(res.as_bytes()); // 读取并处理前两行元数据 if let Ok(record) = reader.records().next().unwrap() { println!("元数据1: {:?}", record); } if let Ok(record) = reader.records().next().unwrap() { println!("元数据2: {:?}", record); } // 读取剩余业务数据行 for result in reader.records() { let record = result?; println!("数据行: {:?}", record) } Ok(()) }
方式2:允许不等长记录
如果需要完整读取所有行,且不想因为字段长度不一致触发报错,可以通过flexible(true)配置csv阅读器,关闭行字段长度一致性检查:
use csv::Error; use csv::ReaderBuilder; use encoding_rs; use std::fs; fn main() -> Result<(), Error> { let path = "./test.csv"; let file = fs::read(path).unwrap(); let (res, _, _) = encoding_rs::SHIFT_JIS.decode(&file); let mut reader = ReaderBuilder::new() .has_headers(false) .flexible(true) // 允许字段长度不一致的记录 .from_reader(res.as_bytes()); for result in reader.records() { let record = result?; println!("{:?}", record) } Ok(()) }
补充说明
flexible(true)适用于格式不规则但需要完整读取的场景,会跳过csv库对行字段长度的校验。- 如果需要将第三行作为数据表头,可调整
has_headers配置为true,结合headers()方法获取表头后再读取数据。
内容的提问来源于stack exchange,提问作者Notebook
相关产品推荐
相关产品推荐

