Работа с CSV в Rust: библиотека csv и практические примеры

Онлайн-тренажер Python для начинающих

Изучайте Python легко и без перегрузки теорией. Решайте практические задачи с автоматической проверкой, получайте подсказки на русском языке и пишите код прямо в браузере — без необходимости что-либо устанавливать.

Начать курс

Библиотека 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)?;
    }                    

Рекомендаций