Csv
Csv module: streaming CSV parser.
Two consumption modes: each_row(path, delimiter, mode, callback) -- streaming; guaranteed cleanup read_all(path, delimiter, mode) -- eager; for small files
Modes: :simple -- split on delimiter only; no quoting :rfc4180 -- full RFC 4180: quoted fields, "" escapes, embedded newlines (including embedded CR / LF / CRLF inside quoted fields)
Line-terminator handling The parser accepts CRLF (RFC 4180), LF (Unix), and bare CR (legacy Mac) as record separators interchangeably. The output strings have trailing CR and LF stripped. Inside :rfc4180 quoted fields, raw CR and LF bytes are passed through verbatim — only the closing " ends the field.
Convenience: read_csv(path) -- comma-delimited, :rfc4180, eager each_csv_row(path, callback) -- comma-delimited, :rfc4180, streaming
Types
Result of csv_next_row: either end-of-file or a parsed row.
Functions
Convenience: comma-delimited, RFC 4180, streaming. Equivalent to each_row(path, ",", :rfc4180, callback).
Examples
Csv.each_csv_row("data.csv", fn fields -> println(String.join(fields, ";")))Stream rows from path one at a time, invoking callback(fields) for each row. delimiter is a one-character String (",", "\\t", …); mode is :simple (split-only, no quoting) or :rfc4180 (full RFC 4180 with quoted fields, "" escapes, embedded newlines).
Only the first character of delimiter is used. Each row's fields are passed in order; trailing CR/LF is stripped.
The file handle is closed when iteration reaches EOF. It is NOT closed if the callback panics (there is no cleanup on that path). Returns Ok(:ok) on success, Err(FileError(message)) if the file cannot be opened.
Examples
Csv.each_row("data.tsv", "\t", :simple, fn fields ->
println(String.join(fields, " | ")))Stream rows treating the first row as a header. Invokes callback(header, fields) for every subsequent row, where header is the parsed first row. An empty file (or a header-only file) yields Ok(:ok) without invoking the callback. Closes the file on EOF, but not if the callback panics. Err(FileError(message)) if the file cannot be opened.
Examples
Csv.each_row_with_header("users.csv", ",", :rfc4180, fn (header, fields) ->
println(int_to_string(List.length(fields)) ++ " of " ++ int_to_string(List.length(header)) ++ " columns"))Eagerly read every row of path into a List(List(String)). Returns Ok(rows) (in file order, one list of fields per record) or Err(FileError(message)) if the file cannot be opened. Use each_row/4 for files that don't comfortably fit in memory.
Examples
-- data.csv: name,qty\n"pen, blue",3
Csv.read_all("data.csv", ",", :rfc4180)
-- Ok([["name", "qty"], ["pen, blue", "3"]])Convenience: comma-delimited, RFC 4180, eager. Equivalent to read_all(path, ",", :rfc4180).
Examples
match Csv.read_csv("data.csv") do
Ok(rows) -> println(int_to_string(List.length(rows)) ++ " rows")
Err(_) -> println("cannot open data.csv")
end