| Title: | Read CSV Files Using the 'zsv' Parser |
|---|---|
| Description: | Reads comma-separated and other delimited text files into base R data frames using the 'zsv' C parser <https://github.com/liquidaty/zsv> as its native backend. Handles quoted fields, embedded delimiters and newlines, escaped quotes, and both LF and CRLF line endings, and infers logical, integer, double, and character column types. Column types can also be given explicitly, in which case a value that cannot be converted is an error rather than a silently missing value. The 'zsv' sources are bundled, so there are no runtime dependencies. |
| Authors: | Pedro Baltazar [aut, cre, cph], Tai Chi Minh Ralph Eastwood [ctb, cph] (Author of the bundled 'zsv' library), Matt Wong [ctb] (Author of the bundled 'zsv' library), Guarnerix Inc dba Liquidaty [cph] (Copyright holder of the bundled 'zsv' library), zsv contributors [ctb, cph] (Contributors to the bundled 'zsv' library), Max Ogden [ctb, cph] (Author of the 'csv-spectrum' test fixtures) |
| Maintainer: | Pedro Baltazar <[email protected]> |
| License: | MIT + file LICENSE |
| Version: | 0.1.0 |
| Built: | 2026-10-16 18:01:25 UTC |
| Source: | https://github.com/cran/zucsv |
Reads a delimited text file into a base R data frame, using the bundled
zsv C parser. The file is read twice: once to establish the shape and
validate it, once to fill columns that were allocated at their exact
final length.
read_csv( file = NULL, header = TRUE, delimiter = ",", na = c("", "NA"), col_types = NULL, text = NULL )read_csv( file = NULL, header = TRUE, delimiter = ",", na = c("", "NA"), col_types = NULL, text = NULL )
file |
Path to a local file, as a single string. |
header |
If |
delimiter |
The field delimiter, as a single ASCII character.
Defaults to |
na |
Character vector of cell values to read as missing, or |
col_types |
|
text |
The CSV itself, as a character vector, instead of a path. A
vector of several elements is joined with newlines, so each element is
one line — |
Some behavior worth knowing before you rely on it:
Blank lines are skipped, wherever they appear, and do not count as
rows. A line holding only whitespace or only delimiters is not blank:
it is a record of empty cells and must match the table's width. In a
single-column file, write an intentional empty cell as "" — a bare
empty line is skipped.
Every record must have the same width. A mismatch is an error; short rows are not padded.
Column names are kept exactly as they appear, including duplicates and empty strings. No name repair is performed.
Text must be valid UTF-8. A byte sequence that is not, or an embedded NUL, is an error rather than something passed through to fail later. A UTF-8 BOM is ignored.
A CR LF pair inside a quoted field is normalised to a single LF.
header = NA looks at the first record only, and at nothing else:
not the rest of the file, not col_types, and not na. It therefore
reaches a different verdict from data.table::fread() and DuckDB, which
compare the first record against the types of the rest, on three shapes
— and the row count moves in both directions:
an unquoted header of numeric-looking names over numeric
columns, such as id,2024,2025 above 1,5,6, reads as data, so
the name record becomes a row and you get one row more;
a first record that is entirely missing values, NA,NA,NA
above 1,2,3, reads as a header, costing one row;
a headerless first record of entirely quoted numbers,
"1","2" above "3","4", reads as a header, costing one row.
Detection is a convenience for files whose shape you do not know. When
you do know, pass header = TRUE or header = FALSE and none of this
applies.
A data.frame with one column per field. Names come from the
first record when header = TRUE, or when header = NA and that record
is detected as names; otherwise they are V1, V2, ... An empty file,
or one of nothing but blank lines, gives a data frame with zero rows and
zero columns; a header-only file gives zero rows but one column per
header field.
sniff_csv(), to see the header verdict and inferred types
before committing to them — and to pin them down so nothing is
inferred on the next run.
# A small CSV given directly, rather than from a file read_csv(text = "a,b\n1,2") # A character vector is one line per element read_csv(text = c("id,name", "1,Ada", "2,Linus")) path <- tempfile(fileext = ".csv") write.csv( data.frame(id = 1:3, name = c("Ada", "Linus", "Grace")), path, row.names = FALSE ) read_csv(path) # Force a column's type rather than inferring it read_csv(path, col_types = c("character", "character")) # Detect whether the first record is a header read_csv(path, header = NA) unlink(path)# A small CSV given directly, rather than from a file read_csv(text = "a,b\n1,2") # A character vector is one line per element read_csv(text = c("id,name", "1,Ada", "2,Linus")) path <- tempfile(fileext = ".csv") write.csv( data.frame(id = 1:3, name = c("Ada", "Linus", "Grace")), path, row.names = FALSE ) read_csv(path) # Force a column's type rather than inferring it read_csv(path, col_types = c("character", "character")) # Detect whether the first record is a header read_csv(path, header = NA) unlink(path)
Runs the first of read_csv()'s two passes and returns what it learned —
the header verdict, the shape, the column names and the inferred column
types — without building any columns. Use it when you want to see the
decisions before committing to them, or to pin them down so a later run
cannot drift:
sniff_csv(file = NULL, delimiter = ",", na = c("", "NA"), text = NULL)sniff_csv(file = NULL, delimiter = ",", na = c("", "NA"), text = NULL)
file |
Path to a local file, as a single string. |
delimiter |
The field delimiter, as a single ASCII character.
Defaults to |
na |
Character vector of cell values to read as missing, or |
text |
The CSV itself, as a character vector, instead of a path, with
the same meaning as in |
s <- sniff_csv(path) read_csv(path, header = s$header, col_types = s$col_types)
That second call reads the file strictly: nothing about it is inferred, so it gives the same answer next month even if the data changes shape.
Sniffing reads the whole file and applies exactly the validation
read_csv() applies, so a ragged row, an embedded NUL or invalid UTF-8 is
an error here too. If sniff_csv() succeeds, read_csv() on the same file
will not fail on content.
It costs one pass rather than two, but that pass is the expensive one: do not call it before every read as a matter of course. Its purpose is to let you stop inferring, not to infer twice.
A list with five elements:
headerTRUE if the first record is names, FALSE if it is
data, by the rule documented under header in read_csv().
ncolNumber of columns.
nrowNumber of data rows — exact, not an estimate, because the pass counts every record.
col_namesThe names the file would produce: the first record
when header is TRUE, otherwise V1, V2, ...
col_typesInferred type of each column, suitable for passing
straight back as col_types.
An empty file gives header = FALSE, zero columns and zero rows.
path <- tempfile(fileext = ".csv") writeLines(c("id,name,score", "1,Ada,9.5", "2,Grace,8"), path) sniff_csv(path) # Pin the decisions, then read without inferring anything s <- sniff_csv(path) read_csv(path, header = s$header, col_types = s$col_types) unlink(path)path <- tempfile(fileext = ".csv") writeLines(c("id,name,score", "1,Ada,9.5", "2,Grace,8"), path) sniff_csv(path) # Pin the decisions, then read without inferring anything s <- sniff_csv(path) read_csv(path, header = s$header, col_types = s$col_types) unlink(path)