Работа с CSV в Rust: библиотека csv и обработка данных

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

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

Начать курс

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

Формат CSV (Comma-Separated Values) остаётся одним из самых популярных способов хранения табличных данных. В Rust для работы с CSV существует зрелая и высокопроизводительная библиотека csv, которая предоставляет удобный API для чтения, записи и обработки данных. В этой статье мы подробно разберём, как эффективно работать с CSV в Rust.

Установка и подключение библиотеки

Для начала добавьте зависимость в файл Cargo.toml:

[dependencies]
csv = "1.3"

Библиотека csv поддерживает работу с кодировками, настраиваемыми разделителями и кавычками, а также предоставляет возможность сериализации/десериализации данных через типажи serde.

Чтение CSV-файла

Самый простой способ прочитать CSV — использовать csv::Reader. Рассмотрим пример с файлом users.csv:

use std::error::Error;
use csv::ReaderBuilder;

fn main() -> Result<(), Box<dyn Error>> {
    // Создаём ридер с настройками
    let mut rdr = ReaderBuilder::new()
        .has_headers(true)      // первая строка — заголовки
        .delimiter(b',')        // разделитель — запятая
        .from_path("users.csv")?;

    // Выводим заголовки
    let headers = rdr.headers()?;
    println!("Headers: {:?}", headers);

    // Итерируемся по записям
    for result in rdr.records() {
        let record = result?;
        println!("{:?}", record);
    }
    Ok(())
}

Обработка записей с помощью десериализации

Для работы со структурированными данными удобно использовать десериализацию в пользовательские структуры. Подключите serde и serde_derive:

[dependencies]
csv = "1.3"
serde = { version = "1.0", features = ["derive"] }

Теперь можно определить структуру и читать данные напрямую:

use serde::Deserialize;

#[derive(Debug, Deserialize)]
struct User {
    name: String,
    age: u8,
    email: String,
}

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let mut rdr = csv::Reader::from_path("users.csv")?;

    for result in rdr.deserialize() {
        let user: User = result?;
        println!("Пользователь: {} ({} лет) - {}", user.name, user.age, user.email);
    }
    Ok(())
}

Запись данных в CSV

Для записи используется csv::Writer. Пример записи структурированных данных:

use csv::Writer;
use serde::Serialize;

#[derive(Debug, Serialize)]
struct Product {
    id: u32,
    name: String,
    price: f64,
    in_stock: bool,
}

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let mut wtr = Writer::from_path("products.csv")?;

    // Записываем заголовки
    wtr.write_record(&["id", "name", "price", "in_stock"])?;

    // Записываем данные через сериализацию
    let products = vec![
        Product { id: 1, name: "Ноутбук".into(), price: 75000.50, in_stock: true },
        Product { id: 2, name: "Мышь".into(), price: 1500.00, in_stock: false },
    ];

    for product in &products {
        wtr.serialize(product)?;
    }

    wtr.flush()?;
    println!("Данные записаны в products.csv");
    Ok(())
}

Настройка разделителей и кавычек

Библиотека позволяет гибко настраивать формат. Например, для TSV (табуляция как разделитель):

let mut rdr = csv::ReaderBuilder::new()
    .delimiter(b'\t')
    .flexible(true) // разрешить разное количество полей
    .quoting(false) // отключить кавычки
    .from_reader(io::stdin());

Обработка ошибок и больших файлов

При работе с большими CSV-файлами важно учитывать производительность. csv использует буферизированное чтение и не загружает весь файл в память. Для обработки ошибок используйте паттерн Result:

fn process_csv(path: &str) -> Result<u64, Box<dyn std::error::Error>> {
    let mut rdr = csv::ReaderBuilder::new()
        .trim(csv::Trim::All) // обрезать пробелы
        .from_path(path)?;

    let mut count = 0u64;
    for result in rdr.records() {
        match result {
            Ok(record) => {
                count +=                    

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