Travailler avec CSV en Rust : bibliothèques, exemples de code et bonnes pratiques
CSV (Comma-Separated Values) est l'un des formats les plus courants pour stocker des données tabulaires. Dans l'écosystème Rust, il existe une bibliothèque puissante et flexible csv qui, combinée à la crate serde, permet de lire, écrire et transformer facilement des données CSV. Dans cet article, nous allons détailler comment travailler avec CSV en Rust, en commençant par l'analyse de base jusqu'à la sérialisation de structures complexes.
Installation et ajout des dépendances
Pour commencer, ajoutez les dépendances suivantes dans votre fichier Cargo.toml :
[dependencies]csv = "1.3"serde = { version = "1.0", features = ["derive"] }La bibliothèque csv fournit une API de bas niveau pour la lecture et l'écriture, tandis que serde permet la désérialisation automatique des lignes CSV en structures Rust.
Lecture d'un fichier CSV ligne par ligne
La façon la plus simple de lire un CSV est d'utiliser Reader de la crate csv. Voici un exemple avec le fichier users.csv :
use csv::Reader;use std::error::Error;
fn main() -> Result<(), Box<dyn Error>> { let mut rdr = Reader::from_path("users.csv")?;
for result in rdr.records() { let record = result?; println!("{:?}", record); } Ok(())}Ce code affiche chaque ligne sous forme d'un vecteur de chaînes StringRecord. Cependant, en pratique, on préfère souvent travailler avec des données typées.
Désérialisation en structures avec serde
Déclarons une structure correspondant aux colonnes du CSV :
use serde::Deserialize;
#[derive(Debug, Deserialize)]struct User { name: String, age: u32, city: String,}On peut maintenant lire les enregistrements directement dans des instances de User :
use csv::Reader;use std::error::Error;
fn main() -> Result<(), Box<dyn Error>> { let mut rdr = Reader::from_path("users.csv")?;
for result in rdr.deserialize() { let user: User = result?; println!("{} ({} ans) de {}", user.name, user.age, user.city); } Ok(())}La bibliothèque fait automatiquement correspondre les en-têtes CSV avec les champs de la structure. Si l'ordre ou les noms des colonnes diffèrent, utilisez l'attribut #[serde(rename = "...")].
Écriture d'un fichier CSV
La création d'un fichier CSV se fait avec Writer. Écriture de données structurées via serde :
use csv::Writer;use serde::Serialize;use std::error::Error;
#[derive(Debug, Serialize)]struct Product { id: u32, name: String, price: f64,}
fn main() -> Result<(), Box<dyn Error>> { let mut wtr = Writer::from_path("products.csv")?;
let products = vec![ Product { id: 1, name: "Ordinateur portable".into(), price: 75000.0 }, Product { id: 2, name: "Souris".into(), price: 1500.0 }, ];
for product in &products { wtr.serialize(product)?; }
wtr.flush()?; println!("Fichier products.csv écrit avec succès"); Ok(())}Notez que Writer ajoute automatiquement les en-têtes basés sur les noms des champs de la structure.
Configuration des séparateurs et des guillemets
Par défaut, la virgule et les guillemets doubles sont utilisés. Si votre fichier utilise le point-virgule (par exemple, dans les locales européennes), configurez ReaderBuilder :
use csv::ReaderBuilder;
let mut rdr = ReaderBuilder::new() .delimiter(b';') .has_headers(true) .from_path("data.tsv")?;De même, WriterBuilder peut être configuré pour l'écriture.