CSV processing
Read CSV records
Reads standard CSV records intocsv::StringRecord
— a weakly typeddata representation which expects valid UTF-8 rows. Alternatively,csv::ByteRecord
makes no assumptions about UTF-8.
use csv::Error;fn main() -> Result<(), Error> { let csv = "year,make,model,description1948,Porsche,356,Luxury sports car1967,Ford,Mustang fastback 1967,American car"; let mut reader = csv::Reader::from_reader(csv.as_bytes()); for record in reader.records() { let record = record?; println!( "In {}, {} built the {} model. It is a {}.", &record[0], &record[1], &record[2], &record[3] ); } Ok(())}
Serde deserializes data into strongly type structures. See thecsv::Reader::deserialize
method.
use serde::Deserialize;#[derive(Deserialize)]struct Record { year: u16, make: String, model: String, description: String,}fn main() -> Result<(), csv::Error> { let csv = "year,make,model,description1948,Porsche,356,Luxury sports car1967,Ford,Mustang fastback 1967,American car"; let mut reader = csv::Reader::from_reader(csv.as_bytes()); for record in reader.deserialize() { let record: Record = record?; println!( "In {}, {} built the {} model. It is a {}.", record.year, record.make, record.model, record.description ); } Ok(())}
Read CSV records with different delimiter
Reads CSV records with a tab [delimiter
].
use csv::Error;use serde::Deserialize;#[derive(Debug, Deserialize)]struct Record { name: String, place: String, #[serde(deserialize_with = "csv::invalid_option")] id: Option<u64>,}use csv::ReaderBuilder;fn main() -> Result<(), Error> { let data = "name\tplace\tid\n\ Mark\tMelbourne\t46\n\ Ashley\tZurich\t92"; let mut reader = ReaderBuilder::new().delimiter(b'\t').from_reader(data.as_bytes()); for result in reader.deserialize::<Record>() { println!("{:?}", result?); } Ok(())}
Filter CSV records matching a predicate
Returnsonly the rows fromdata
with a field that matchesquery
.
use anyhow::Result;use std::io;fn main() -> Result<()> { let query = "CA"; let data = "\City,State,Population,Latitude,LongitudeKenai,AK,7610,60.5544444,-151.2583333Oakman,AL,,33.7133333,-87.3886111Sandfort,AL,,32.3380556,-85.2233333West Hollywood,CA,37031,34.0900000,-118.3608333"; let mut rdr = csv::ReaderBuilder::new().from_reader(data.as_bytes()); let mut wtr = csv::Writer::from_writer(io::stdout()); wtr.write_record(rdr.headers()?)?; for result in rdr.records() { let record = result?; if record.iter().any(|field| field == query) { wtr.write_record(&record)?; } } wtr.flush()?; Ok(())}
Disclaimer: this example has been adapted fromthe csv crate tutorial.
Handle invalid CSV data with Serde
CSV files often contain invalid data. For these cases, thecsv
crateprovides a custom deserializer,csv::invalid_option
, which automaticallyconverts invalid data to None values.
use csv::Error;use serde::Deserialize;#[derive(Debug, Deserialize)]struct Record { name: String, place: String, #[serde(deserialize_with = "csv::invalid_option")] id: Option<u64>,}fn main() -> Result<(), Error> { let data = "name,place,idmark,sydney,46.5ashley,zurich,92akshat,delhi,37alisha,colombo,xyz"; let mut rdr = csv::Reader::from_reader(data.as_bytes()); for result in rdr.deserialize() { let record: Record = result?; println!("{:?}", record); } Ok(())}
Serialize records to CSV
This example shows how to serialize a Rust tuple.csv::writer
supports automaticserialization from Rust types into CSV records.write_record
writesa simple record containing string data only. Data with more complex valuessuch as numbers, floats, and options useserialize
. Since CSVwriter uses internal buffer, always explicitlyflush
when done.
use anyhow::Result;use std::io;fn main() -> Result<()> { let mut wtr = csv::Writer::from_writer(io::stdout()); wtr.write_record(&["Name", "Place", "ID"])?; wtr.serialize(("Mark", "Sydney", 87))?; wtr.serialize(("Ashley", "Dublin", 32))?; wtr.serialize(("Akshat", "Delhi", 11))?; wtr.flush()?; Ok(())}
Serialize records to CSV using Serde
The following example shows how to serialize custom structs as CSV records usingtheserde crate.
use anyhow::Result;use serde::Serialize;use std::io;#[derive(Serialize)]struct Record<'a> { name: &'a str, place: &'a str, id: u64,}fn main() -> Result<()> { let mut wtr = csv::Writer::from_writer(io::stdout()); let rec1 = Record { name: "Mark", place: "Melbourne", id: 56}; let rec2 = Record { name: "Ashley", place: "Sydney", id: 64}; let rec3 = Record { name: "Akshat", place: "Delhi", id: 98}; wtr.serialize(rec1)?; wtr.serialize(rec2)?; wtr.serialize(rec3)?; wtr.flush()?; Ok(())}# Transform CSV column[![csv-badge]][csv] [![serde-badge]][serde] [![cat-encoding-badge]][cat-encoding]Transform a CSV file containing a color name and a hex color into one with acolor name and an rgb color. Utilizes the [csv] crate to read and write thecsv file, and [serde] to deserialize and serialize the rows to and from bytes.See [csv::Reader::deserialize], [serde::Deserialize], and [std::str::FromStr]```rust,edition2018use anyhow::{Result, anyhow};use csv::{Reader, Writer};use serde::{de, Deserialize, Deserializer};use std::str::FromStr;#[derive(Debug)]struct HexColor { red: u8, green: u8, blue: u8,}#[derive(Debug, Deserialize)]struct Row { color_name: String, color: HexColor,}impl FromStr for HexColor { type Err = anyhow::Error; fn from_str(hex_color: &str) -> std::result::Result<Self, Self::Err> { let trimmed = hex_color.trim_matches('#'); if trimmed.len() != 6 { Err(anyhow!("Invalid length of hex string")) } else { Ok(HexColor { red: u8::from_str_radix(&trimmed[..2], 16)?, green: u8::from_str_radix(&trimmed[2..4], 16)?, blue: u8::from_str_radix(&trimmed[4..6], 16)?, }) } }}impl<'de> Deserialize<'de> for HexColor { fn deserialize<D>(deserializer: D) -> std::result::Result<Self, D::Error> where D: Deserializer<'de>, { let s = String::deserialize(deserializer)?; FromStr::from_str(&s).map_err(de::Error::custom) }}fn main() -> Result<()> { let data = "color_name,colorred,#ff0000green,#00ff00blue,#0000FFperiwinkle,#ccccffmagenta,#ff00ff" .to_owned(); let mut out = Writer::from_writer(vec![]); let mut reader = Reader::from_reader(data.as_bytes()); for result in reader.deserialize::<Row>() { let res = result?; out.serialize(( res.color_name, res.color.red, res.color.green, res.color.blue, ))?; } let written = String::from_utf8(out.into_inner()?)?; assert_eq!(Some("magenta,255,0,255"), written.lines().last()); println!("{}", written); Ok(())}