(chibi csv)

CSV Grammars

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.

(csv-grammar spec)

Creates a new CSV grammar from the given spec, an alist of symbols to values. The following options are supported:

Example Gecos grammar:

(csv-grammar
  '((separator-chars #\:)
    (quote-char . #f)))
=> #<Csv-Grammar 137012760698432>

default-csv-grammar

The default CSV grammar for convenience, with all of the defaults from csv-grammar, i.e. comma-delimited with #} for quoting, doubled to escape.

default-tsv-grammar

The default TSV grammar for convenience, splitting fields only on tabs, with no quoting or escaping.

CSV Parsers

csv-parser

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)

(csv-skip-line in grammar)

csv-num-rows

Returns the number of rows in the input.

CSV Readers

csv-read->list

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")

csv-read->vector

The equivalent of csv-read->list but returns a vector.

((csv-read->vector) (open-input-string "foo,bar,baz"))
=> #("foo" "bar" "baz")

csv-read->fixed-vector

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")

csv-read->sxml

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"))

CSV Utilities

csv-fold

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")

csv-for-each

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)
=> 3

csv-map

Returns 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)

csv->list

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"))

csv->sxml

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")))

CSV Writers

csv-writer

csv-write