std/csv
import std/csv to use it. Source: src/std/csv.iyi at v0.18.0.
CSV.parse reads a document into rows of fields. A field may be quoted; a quote inside a quoted field is doubled. The separator is a comma. Lines end at LF, CRLF or a lone CR, and a blank line is a row of no fields, as in the other library. CSV.build writes the same contract: a field is quoted when it holds a comma, a quote or a line break, a row of one empty field is written "", and a last row of no fields is ended with a line break. A quote without its pair is unterminated quoted field; text after a closing quote, and a quote in a field that did not open with one, are refused at their line and column, as the other library refuses them. A byte order mark is read past, and a UTF-16 document is refused by its.
CSVReader reads the same rules a row at a time, from a String or an IyiIO, so a file far larger than memory is read through a 64 KiB window and one field's buffer, both reused: next_row gives the fields, next a CSVRow that also answers by the header's names when the reader was made with headers: true (the first row; a later name of the same text wins, and blank lines are passed over, as Python's csv.DictReader has them). The separator and the quote are the caller's, one ASCII character each, neither a line end and not the same one; strip: true drops spaces and tabs around a field that is not quoted and around a quoted one's quotes; same_size: true refuses a row whose field count is not the first row's (or the header's).
What the reader is given comes from outside, so what is wrong with it is a CSVError, a value with the line and the column (in characters, from 1, a CRLF one line end) where it went wrong: a quote inside a field that did not open with one, text after a closing quote, a quote still open at the end (named where it opened), a row of the wrong size under same_size, a UTF-16 byte order mark, a field past what a string holds, and a stream whose read failed. After one, the reader answers the same error again: where the next row begins is not knowable. CSV.parse keeps panicking with the same sentences, as it always has.
CSVWriter writes rows to an IyiIO, each ended by its line end (LF, or CRLF as RFC 4180 has it, or CR). A field is quoted when it holds the separator, the quote or a line break, and when it starts or ends with a space or a tab, which a strip: true reader would otherwise drop; a row of one empty field is "" (bare, it is a blank line, a row of none), and a first field that opens with U+FEFF is quoted so it is not read past as a byte order mark. quote_all: true quotes every field. Python's csv module reads what it writes, and it reads what Python's writes; bench/std_csv_exercise.sh holds both directions on a generated corpus.
CSVstruct
pub struct CSV
# Into one builder: adding each field to the text so far copied that
# text once per field, and 20,000 rows of five took 12.8 seconds.
def self.build(rows : Array(Array(String))) : String
def initialize
def self.parse(text : String) : Array(Array(String))
endCSVErrorstruct
What is wrong with a document, and where: line and column count from 1, the column in characters.
pub struct CSVError
def column : Int32
def initialize(message : String, line : Int32, column : Int32)
def line : Int32
def to_s : String
endCSVReaderclass
Rows read one at a time from a String or an IyiIO.
reader = CSVReader.new(File.open("people.csv"), headers: true)
while row = reader.next.or_panic
puts row["name"]
endpub class CSVReader
# Each row left, in order; the error that stopped them, if one did.
def each(& : (CSVRow -> Nil)) : Nil | CSVError
# The header row, read now if no row has been. Only for a reader made
# with `headers: true`: on any other, the first row is a row.
def headers : Array(String) | CSVError
def initialize(text : String, separator : Char = ',', quote : Char = '"', headers : Bool = false, strip : Bool = false, same_size : Bool = false)
# Reads *io* as far as each row needs, CHUNK bytes at a time; it is not
# closed.
def initialize(io : IyiIO, separator : Char = ',', quote : Char = '"', headers : Bool = false, strip : Bool = false, same_size : Bool = false)
# The next row with its header's names, nil past the last.
def next : CSVRow | CSVError | Nil
# The next row's fields, nil past the last, or what is wrong with the
# document there. A blank line is a row of no fields, except under a
# header, where it is passed over.
def next_row : Array(String) | CSVError | Nil
endCSVRowclass
One row as CSVReader#next gives it: its fields, by position or by the header's names.
pub class CSVRow
def [](at : Int32) : String
# The field under the header *name*: "" when this row is too short to
# have one, which is the document's doing. A name the header does not
# have is the program's, and panics.
def [](name : String) : String
def []?(at : Int32) : String | ::Nil
# The field under the header *name*, or nil when the header has no such
# name or this row is too short to reach it.
def []?(name : String) : String | ::Nil
def fields : Array(String)
# The header row, or none when the reader was made without one.
def headers : Array(String)
def initialize(fields : Array(String), headers : Array(String), index : Hash(String, Int32), line : Int32)
# The line the row starts on.
def line : Int32
def size : Int32
# The header's names to this row's fields, as far as both go.
def to_h : Hash(String, String)
endCSVWriterclass
Rows written to an IyiIO, quoted where a reader needs them to be.
out = CSVWriter.new(STDOUT, line_end: "\r\n")
out.row(["name", "says"])
out.row(["Ada", "\"hello\", she said"])pub class CSVWriter
def flush : Nil
def initialize(io : IyiIO, separator : Char = ',', quote : Char = '"', quote_all : Bool = false, line_end : String = "\n")
# One row, and its line end.
def row(fields : Array(String)) : Nil
# The rows written so far.
def rows : Int32
endError for CSVErrorimpl
impl Error for CSVError
def message : String
end
CSV, RFC 4180, written in iyi.