csv API reference
RFC 4180 comma-separated values: parse text into rows of fields and format rows back into text, with a quoting-aware hand-written scanner. Pure Jennifer - no Go, no system library. The delimiter is configurable, so the same code reads and writes TSV and other single-character-separated formats. Records separate on LF or CRLF (a bare CR outside quotes also ends a record); a field is quoted with " when it contains the delimiter, a quote, or a newline, and an embedded quote doubles to "". format() joins records with LF and adds no trailing newline, so parse(format(rows)) round-trips the data.
Import with import "csv.j" as csv;. See the csv guide for prose and examples.
Functions
csv.closeReader(reader as Reader)
Close a streaming reader (closes the underlying file handle).
Parameters
reader{Reader}- the reader
csv.closeWriter(writer as Writer)
Close a streaming writer (closes the underlying file handle).
Parameters
writer{Writer}- the writer
csv.dialect(delimiter as string)
A Dialect with the given delimiter and the standard defaults: quote ", no comment prefix, no trimming. Set the other fields to customise.
Parameters
delimiter{string}- the single-character field delimiter
Returns {Dialect} - the dialect
csv.format(rows as list of list of string)
Join rows into standard comma-delimited CSV. Not safe for a spreadsheet target - use formatSafe when the output may be opened in Excel / Sheets (see formatWith on the CWE-1236 formula-injection risk).
Parameters
rows{list of list of string}- the rows of fields to format
Returns {string} - the formatted comma-separated text
csv.formatDialect(rows as list of list of string, d as Dialect)
Format rows under a Dialect (custom delimiter and quote character). Records are joined with LF and no trailing newline, like formatWith. The comment and trim fields are parse-only and ignored here.
Parameters
rows{list of list of string}- the rows of fields to formatd{Dialect}- the dialect controlling delimiter/quote
Returns {string} - the formatted text
csv.formatSafe(rows as list of list of string)
Injection-safe standard comma-delimited CSV (see formatSafeWith). The export-to-spreadsheet path; prefer it over format for spreadsheet targets.
Parameters
rows{list of list of string}- the rows of fields to format
Returns {string} - the formatted, injection-neutralised comma-separated text
csv.formatSafeWith(rows as list of list of string, delim as string)
Like formatWith, but neutralises spreadsheet formula injection (CWE-1236) before quoting: any field whose first character is =, +, -, @, a tab, or a CR is prefixed with a single apostrophe so Excel / Google Sheets import it as literal text instead of executing it. Use this - not formatWith - for any CSV a spreadsheet will open.
Parameters
rows{list of list of string}- the rows of fields to formatdelim{string}- the single-character field delimiter
Returns {string} - the formatted, injection-neutralised text
csv.formatWith(rows as list of list of string, delim as string)
Join rows into text with a single-character delimiter, quoting each field as needed. Records are separated by LF with no trailing newline.
Not safe for a spreadsheet target. A field beginning = + - @ (or a tab / CR) is a formula that Excel / Google Sheets will execute on open (CWE-1236). If the output is opened as a spreadsheet, use formatSafe / formatSafeWith, which neutralise such fields.
Parameters
rows{list of list of string}- the rows of fields to formatdelim{string}- the single-character field delimiter
Returns {string} - the formatted delimiter-separated text
csv.fromRecords(header as list of string, records as list of map of string to string)
The inverse of toRecords: emit the header row followed by one row per record, taking fields in header order (a key absent from a record writes ""). The explicit header fixes the column order, which map iteration does not.
Parameters
header{list of string}- the column names, in output orderrecords{list of map of string to string}- the records to emit
Returns {list of list of string} - the header row followed by one row per record
csv.parse(s as string)
Scan standard comma-delimited CSV.
Parameters
s{string}- the CSV text to parse
Returns {list of list of string} - the parsed rows of fields
csv.parseDialect(text as string, d as Dialect)
Parse CSV text under a Dialect: custom delimiter and quote, comment-line skipping, and optional unquoted-field trimming. Quoted fields keep their content (including whitespace and embedded newlines) verbatim.
Parameters
text{string}- the CSV text to parsed{Dialect}- the dialect controlling delimiter/quote/comment/trim
Returns {list of list of string} - the parsed rows of fields
csv.parseWith(s as string, delim as string)
Scan CSV text with a single-character delimiter and return its rows, each a list of string fields. A quoted field may span the delimiter, newlines, and doubled quotes; an empty input yields no rows.
Parameters
s{string}- the CSV text to parsedelim{string}- the single-character field delimiter
Returns {list of list of string} - the parsed rows of fields
csv.readRow(reader as Reader)
Read one complete CSV record from the reader, returning its fields. A record whose quoted field spans physical lines is read to completion (further lines are pulled until the quotes balance). Returns the empty list [] at end-of-file (guard the loop with readerEof) and for a blank line.
Parameters
reader{Reader}- the reader
Returns {list of string} - the record's fields, or [] at EOF / on a blank line
csv.reader(file as fs.File)
Wrap an open, read-mode fs.File as a comma-delimited streaming reader.
Parameters
file{fs.File}- an open read-mode file handle
Returns {Reader} - the reader
csv.readerEof(reader as Reader)
Whether the reader has reached end-of-file. Loop while (not readerEof($r)).
Parameters
reader{Reader}- the reader
Returns {bool} - true once no further record remains
csv.readerWith(file as fs.File, delim as string)
Wrap an open, read-mode fs.File as a streaming reader with a custom single-character delimiter.
Parameters
file{fs.File}- an open read-mode file handledelim{string}- the single-character field delimiter
Returns {Reader} - the reader
csv.toRecords(rows as list of list of string)
Treat the first row as a header and map each later row into a map of string to string keyed by the header names. Every record carries every header key (a short row fills missing fields with ""); fields past the header width are dropped. An empty input yields no records.
Parameters
rows{list of list of string}- the rows, with the first as the header
Returns {list of map of string to string} - one record per non-header row
csv.writeRow(writer as Writer, fields as list of string)
Append one record to the writer: the fields, quoted as needed, followed by LF.
Parameters
writer{Writer}- the writerfields{list of string}- the record's fields
csv.writer(file as fs.File)
Wrap an open write/append-mode fs.File as a comma-delimited streaming writer.
Parameters
file{fs.File}- an open write- or append-mode file handle
Returns {Writer} - the writer
csv.writerWith(file as fs.File, delim as string)
Wrap an open write/append-mode fs.File as a streaming writer with a custom single-character delimiter.
Parameters
file{fs.File}- an open write- or append-mode file handledelim{string}- the single-character field delimiter
Returns {Writer} - the writer
Structs
csv.Dialect
A CSV dialect: the field delimiter, the quote character, an optional comment-line prefix (a physical line whose record starts with it is skipped on parse; "" disables comments), and whether unquoted fields are whitespace- trimmed on parse. Build one with dialect(delimiter) for the common defaults.
| Field | Type | Description |
|---|---|---|
delimiter | string | the single-character field delimiter |
quote | string | the quote character (default ") |
comment | string | the comment-line prefix, or "" for none |
trim | bool | trim leading/trailing whitespace from unquoted fields |
csv.Reader
A streaming CSV reader over an open fs.File, for tables too large to hold in memory. The wrapped file is a handle - it shares its read position across value copies - so successive readRow calls advance the same stream.
| Field | Type | Description |
|---|---|---|
file | fs.File | the underlying open file handle |
delim | string | the single-character field delimiter |
csv.Writer
A streaming CSV writer over an open fs.File. Each writeRow call appends one quoted-as-needed record terminated by LF, so a large table is written without building the whole text in memory.
| Field | Type | Description |
|---|---|---|
file | fs.File | the underlying open write/append-mode file handle |
delim | string | the single-character field delimiter |