Parse a CSV-encoded string into a DataFrame. The pipeline is:
- NyaCSV tokenises the text into headers + rows. NyaCSV always
treats the first row as a header, so the has_header = false
case re-folds that row into the data section and synthesises
column1, column2, … names.
- Per-column type inference walks the first infer_schema_rows
rows in order Int → Float → Bool → String (a null cell within
them is skipped but still counts toward the window). The
first option that accepts every probed cell wins; a column
with no non-null probes lands on String (no information to
disambiguate).
- Null mapping replaces any raw cell whose verbatim string sits
in null_values with None. The mapping runs after
tokenisation, so quoted empty cells ("") and bare empty
cells are both honoured.
- Each column is assembled into a typed Series and the result
is wrapped through DataFrame::from_parts (an empty input
short-circuits to DataFrame::DataFrame([]) before this point).
Raises:
- DuplicateColumn(name) — two headers share a name.
- ParseError(Message(...)) — when strict_quotes is true, the input contains
an unterminated quoted field, text after a closing quote, or a quote
inside an unquoted field; or — when options.strict_column_count is
true — a data row's cell count differs from the header's (reported as
row N: expected C columns, got G, 1-based over the data rows).
- ParseError(Cell(...)) — a non-null cell does not parse under the
inferred dtype for its column (typically because a sample
row outside the inference window is malformed for the inferred
type), unless options.on_parse_error is Null, which downgrades
that cell to a null instead of failing the read.
- Whatever DataFrame::from_parts propagates — DuplicateColumn /
LengthMismatch / a negative-height InvalidOperation, none of which
can actually fire here (uniqueness is checked above; every column
is built off the same non-negative rows.length()).
By default, tokenisation keeps NyaCSV's lenient quote handling. An
unterminated quoted field at end-of-input is read as a complete field
(1,"unclosed → last cell unclosed); text after a closing quote is
folded into the same field ("ab"cd → abcd); and a lone quote inside
an unquoted field is kept literally (ab"cd → ab"cd). A strict
RFC-4180 reader rejects all three; CsvReadOptions::CsvReadOptions(strict_quotes=true)
runs a linear validation pass before tokenisation and rejects them as
ParseError.
projection narrows the read to the named columns — the same knob the
lazy scan_csv source's projection pushdown rides. None (the default)
builds every column; Some(names) builds only the columns whose header
name appears in names, in the file's own header order, leaving the rest
uninferred, unparsed, and unmaterialised. A requested name that matches
no header is silently ignored (lenient projection, as in Polars), and a
projection matching no header builds no column at all and yields the
N×0 frame — the file's rows are still counted, so a downstream head /
slice bounds-checks against the real height. Whole-input checks (strict
quote validation, the empty-input short-circuit, duplicate-header
rejection, the ragged-row guard) run before the projection filter and so
are unaffected by it; the one behavioural difference is the point of the
knob — a parse error confined to a dropped column is not raised, that
column is never parsed.