CSV is a simple and compact format for tabular data, which has made it popular for a variety of tasks since the early days of computing. Unfortunately, there are many incompatible dialects requiring a grammar to specify all of the different options.
Creates a new CSV grammar from the given spec, an alist of symbols to values. The following options are supported:
'separator-chars- A non-empty list of characters used to delimit fields, by default'(#\,)(comma-separated).'quote-char- A single character used to quote fields containing special characters, or#fto disable quoting, by default#\"(a double-quote).'quote-doubling-escapes?- If true, two successivequote-chars within quotes are treated as a single escapedquote-char(default true).'escape-char- A single character used to escape characters within quoted fields, or#fto disable escapes, by default#f(no explicit escape, use quote doubling).'record-separator- A single character used to delimit the record (row), or one of the symbols'cr,'crlf,'lfor'lax. These correspond to sequences of carriage return and line feed, or in the case of'laxany of the other three sequences. Defaults to'lax.'comment-chars- A list of characters which if found at the start of a record indicate it is a comment, discarding all characters through to the next record-separator. Defaults to the empty list (no comments).
Example Gecos grammar:
(csv-grammar
'((separator-chars #\:)
(quote-char . #f)))
=> #<Csv-Grammar 137012760698432>The default CSV grammar for convenience, with all of the defaults
from csv-grammar, i.e. comma-delimited with #}
for quoting, doubled to escape.
The default TSV grammar for convenience, splitting fields only on tabs, with no quoting or escaping.
Parsers are low-level utilities to perform operations on records a
field at a time. You generally want to work with readers, which
build on this to build records into familiar data structures.
Parsers follow the rules of a grammar to parse a single CSV
record, possible comprised of multiple fields. A parser is a
procedure of three arguments which performs a fold operation over
the fields of the record. The parser signature is:
(parser kons knil in), where kons itself is
a procedure of three arguments: (proc acc index field).
proc is called on each field of the record, in order,
along with its zero-based index and the accumulated
result of the last call, starting with knil.
Returns a new CSV parser for the given grammar. The parser
by itself can be used to parse a record at a time.
(let ((parse (csv-parser)))
(parse (lambda (vec i field) (vector-set! vec i (string->number field)) vec)
(make-vector 3)
(open-input-string "1,2,3")))
=> #(1 2 3)Returns the number of rows in the input.
A CSV reader reads a single record, returning some representation of it. You can either loop manually with these or pass them to one of the high-level utilities to operate on a whole CSV file at a time. The simplest reader, simply returns the field string values in order as a list.
((csv-read->list) (open-input-string "foo,bar,baz"))
=> ("foo" "bar" "baz")The equivalent of csv-read->list but returns a vector.
((csv-read->vector) (open-input-string "foo,bar,baz"))
=> #("foo" "bar" "baz")The same as csv-read->vector but requires the vector to
be of a fixed size, and may be more efficient.
((csv-read->fixed-vector 3) (open-input-string "foo,bar,baz"))
=> #("foo" "bar" "baz")Returns an SXML representation of the record, as a row with multiple named columns.
((csv-read->sxml 'city '(name latitude longitude))
(open-input-string "Tokyo,35°41′23″N,139°41′32″E"))
=> (city (name "Tokyo") (latitude "35°41′23″N") (longitude "139°41′32″E"))
A folding operation on records. proc is called successively
on each row and the accumulated result.
(csv-fold
(lambda (row acc) (cons (cadr (assq 'name (cdr row))) acc))
'()
(csv-read->sxml 'city '(name latitude longitude))
(open-input-string
"Tokyo,35°41′23″N,139°41′32″E
Paris,48°51′24″N,2°21′03″E"))
=> ("Paris" "Tokyo")An iterator which simply calls proc on each record in the
input in order.
(let ((count 0))
(csv-for-each
(lambda (row) (if (string->number (car row)) (set! count (+ 1 count))))
(csv-read->list)
(open-input-string
"1,foo\n2,bar\nthree,baz\n4,qux"))
count)
=> 3Returns a list containing the result of calling proc on each
element in the input.
(csv-map
(lambda (row) (string->symbol (cadr row)))
(csv-read->list)
(open-input-string
"1,foo\n2,bar\nthree,baz\n4,qux"))
=> (foo bar baz qux)Returns a list of all of the read records in the input.
(csv->list
(csv-read->list)
(open-input-string
"1,foo\n2,bar\nthree,baz\n4,qux"))
=> (("1" "foo") ("2" "bar") ("three" "baz") ("4" "qux"))Returns an SXML representation of the CSV.
((csv->sxml 'city '(name latitude longitude))
(open-input-string
"Tokyo,35°41′23″N,139°41′32″E
Paris,48°51′24″N,2°21′03″E"))
=> (*TOP* (city (name "Tokyo") (latitude "35°41′23″N") (longitude "139°41′32″E")) (city (name "Paris") (latitude "48°51′24″N") (longitude "2°21′03″E")))