Библиотека csv для работы с CSV-файлами в Rust
CSV (Comma-Separated Values) — один из самых распространённых форматов для хранения табличных данных. В Rust для работы с CSV существует мощная, гибкая и производительная библиотека csv, которая является частью экосистемы BurntSushi (автора ripgrep и regex). Она позволяет читать, записывать, сериализовать и десериализовать данные с минимальными усилиями и максимальной безопасностью типов.
Установка
Добавьте зависимость в Cargo.toml:
[dependencies]
csv = "1.3"
Для сериализации/десериализации структур также потребуется serde:
[dependencies]
csv = "1.3"
serde = { version = "1.0", features = ["derive"] }
Основные возможности библиотеки
- Чтение и запись с настраиваемыми разделителями, кавычками и escape-символами.
- Автоматическая обработка заголовков — работа по именам столбцов.
- Сериализация/десериализация через
serde— преобразование строк CSV в строго типизированные структуры Rust. - Гибкая обработка ошибок — позиционирование ошибок (номер строки, колонки).
- Потоковая обработка — работа с большими файлами без загрузки всего в память.
- Поддержка non-UTF8 кодировок (через внешние крейты, например
encoding).
Чтение CSV-файла
Простейший способ прочитать CSV — использовать Reader и итератор по записям.
Пример 1: Чтение без заголовков
use csv::ReaderBuilder;
use std::error::Error;
fn read_csv_no_headers() -> Result<(), Box<dyn Error>> {
let data = "name,age,city\nAlice,30,London\nBob,25,Paris";
let mut reader = ReaderBuilder::new()
.has_headers(false)
.from_reader(data.as_bytes());
for result in reader.records() {
let record = result?;
println!("{:?}", record);
}
Ok(())
}
Пример 2: Чтение с заголовками
use csv::Reader;
use std::error::Error;
fn read_csv_with_headers() -> Result<(), Box<dyn Error>> {
let data = "name,age,city\nAlice,30,London\nBob,25,Paris";
let mut reader = Reader::from_reader(data.as_bytes());
for result in reader.deserialize() {
let record: (String, u32, String) = result?;
println!("{} is {} years old from {}", record.0, record.1, record.2);
}
Ok(())
}
Использование Serde для строгой типизации
Наиболее удобный способ — определить структуру, аннотированную #[derive(Deserialize)].
use serde::Deserialize;
use csv::Reader;
use std::error::Error;
#[derive(Debug, Deserialize)]
struct Person {
name: String,
age: u32,
city: String,
}
fn read_typed_csv() -> Result<(), Box<dyn Error>> {
let data = "name,age,city\nAlice,30,London\nBob,25,Paris";
let mut reader = Reader::from_reader(data.as_bytes());
for result in reader.deserialize() {
let person: Person = result?;
println!("{:?}", person);
}
Ok(())
}
Это гарантирует, что данные будут автоматически приведены к нужным типам. Если, например, в поле age попадёт строка "abc", программа вернёт ошибку с указанием строки и колонки.
Запись CSV-файла
Для записи используется Writer. Можно записывать как сырые строки, так и сериализованные структуры.
Пример: запись через структуру
use serde::Serialize;
use csv::Writer;
use std::error::Error;
#[derive(Debug, Serialize)]
struct Person {
name: String,
age: u32,
city: String,
}
fn write_csv() -> Result<(), Box<dyn Error>> {
let mut writer = Writer::from_path("output.csv")?;
// Записываем заголовки вручную (опционально)
writer.write_record(&["name", "age", "city"])?;
let people = vec![
Person { name: "Alice".into(), age: 30, city: "London".into() },
Person { name: "Bob".into(), age: 25, city: "Paris".into() },
];
for person in &people {
writer.serialize(person)?;
}