Source Edit

This module implements a simple high performance CSV (comma separated value) parser.

Basic usage

  1. import std/parsecsv
  2. from std/os import paramStr
  3. from std/streams import newFileStream
  4. var s = newFileStream(paramStr(1), fmRead)
  5. if s == nil:
  6. quit("cannot open the file" & paramStr(1))
  7. var x: CsvParser
  8. open(x, s, paramStr(1))
  9. while readRow(x):
  10. echo "new row: "
  11. for val in items(x.row):
  12. echo "##", val, "##"
  13. close(x)

For CSV files with a header row, the header can be read and then used as a reference for item access with rowEntry:

  1. import std/parsecsv
  2. # Prepare a file
  3. let content = """One,Two,Three,Four
  4. 1,2,3,4
  5. 10,20,30,40
  6. 100,200,300,400
  7. """
  8. writeFile("temp.csv", content)
  9. var p: CsvParser
  10. p.open("temp.csv")
  11. p.readHeaderRow()
  12. while p.readRow():
  13. echo "new row: "
  14. for col in items(p.headers):
  15. echo "##", col, ":", p.rowEntry(col), "##"
  16. p.close()

See also

Imports

lexbase, streams, os

Types

  1. CsvError = object of IOError

An exception that is raised if a parsing error occurs. Source Edit

  1. CsvParser = object of BaseLexer
  2. row*: CsvRow
  3. headers*: seq[string]

The parser object.

It consists of two public fields:

  • row is the current row
  • headers are the columns that are defined in the csv file (read using readHeaderRow). Used with rowEntry).

Source Edit

  1. CsvRow = seq[string]

A row in a CSV file. Source Edit

Procs

  1. proc close(self: var CsvParser) {.inline, ...raises: [IOError, OSError],
  2. tags: [WriteIOEffect], forbids: [].}

Closes the parser self and its associated input stream. Source Edit

  1. proc open(self: var CsvParser; filename: string; separator = ','; quote = '\"';
  2. escape = '\x00'; skipInitialSpace = false) {.
  3. ...raises: [CsvError, IOError, OSError], tags: [ReadIOEffect], forbids: [].}

Similar to the other open proc, but creates the file stream for you.

Example:

  1. from std/os import removeFile
  2. writeFile("tmp.csv", "One,Two,Three\n1,2,3\n10,20,300")
  3. var parser: CsvParser
  4. parser.open("tmp.csv")
  5. parser.close()
  6. removeFile("tmp.csv")

Source Edit

  1. proc open(self: var CsvParser; input: Stream; filename: string; separator = ',';
  2. quote = '\"'; escape = '\x00'; skipInitialSpace = false) {.
  3. ...raises: [IOError, OSError], tags: [ReadIOEffect], forbids: [].}

Initializes the parser with an input stream. Filename is only used for nice error messages. The parser’s behaviour can be controlled by the diverse optional parameters:

  • separator: character used to separate fields
  • quote: Used to quote fields containing special characters like separator, quote or new-line characters. ‘\0’ disables the parsing of quotes.
  • escape: removes any special meaning from the following character; ‘\0’ disables escaping; if escaping is disabled and quote is not ‘\0’, two quote characters are parsed one literal quote character.
  • skipInitialSpace: If true, whitespace immediately following the separator is ignored.

See also:

  • open proc which creates the file stream for you

Example:

  1. import std/streams
  2. var strm = newStringStream("One,Two,Three\n1,2,3\n10,20,30")
  3. var parser: CsvParser
  4. parser.open(strm, "tmp.csv")
  5. parser.close()
  6. strm.close()

Source Edit

  1. proc processedRows(self: var CsvParser): int {.inline, ...raises: [], tags: [],
  2. forbids: [].}

Returns number of the processed rows.

But even if readRow arrived at EOF then processed rows counter is incremented.

Example:

  1. import std/streams
  2. var strm = newStringStream("One,Two,Three\n1,2,3")
  3. var parser: CsvParser
  4. parser.open(strm, "tmp.csv")
  5. doAssert parser.readRow()
  6. doAssert parser.processedRows() == 1
  7. doAssert parser.readRow()
  8. doAssert parser.processedRows() == 2
  9. ## Even if `readRow` arrived at EOF then `processedRows` is incremented.
  10. doAssert parser.readRow() == false
  11. doAssert parser.processedRows() == 3
  12. doAssert parser.readRow() == false
  13. doAssert parser.processedRows() == 4
  14. parser.close()
  15. strm.close()

Source Edit

  1. proc readHeaderRow(self: var CsvParser) {....raises: [IOError, OSError, CsvError],
  2. tags: [ReadIOEffect], forbids: [].}

Reads the first row and creates a look-up table for column numbers See also:

Example:

  1. import std/streams
  2. var strm = newStringStream("One,Two,Three\n1,2,3")
  3. var parser: CsvParser
  4. parser.open(strm, "tmp.csv")
  5. parser.readHeaderRow()
  6. doAssert parser.headers == @["One", "Two", "Three"]
  7. doAssert parser.row == @["One", "Two", "Three"]
  8. doAssert parser.readRow()
  9. doAssert parser.headers == @["One", "Two", "Three"]
  10. doAssert parser.row == @["1", "2", "3"]
  11. parser.close()
  12. strm.close()

Source Edit

  1. proc readRow(self: var CsvParser; columns = 0): bool {.
  2. ...raises: [IOError, OSError, CsvError], tags: [ReadIOEffect], forbids: [].}

Reads the next row; if columns > 0, it expects the row to have exactly this many columns. Returns false if the end of the file has been encountered else true.

Blank lines are skipped.

Example:

  1. import std/streams
  2. var strm = newStringStream("One,Two,Three\n1,2,3\n\n10,20,30")
  3. var parser: CsvParser
  4. parser.open(strm, "tmp.csv")
  5. doAssert parser.readRow()
  6. doAssert parser.row == @["One", "Two", "Three"]
  7. doAssert parser.readRow()
  8. doAssert parser.row == @["1", "2", "3"]
  9. ## Blank lines are skipped.
  10. doAssert parser.readRow()
  11. doAssert parser.row == @["10", "20", "30"]
  12. var emptySeq: seq[string]
  13. doAssert parser.readRow() == false
  14. doAssert parser.row == emptySeq
  15. doAssert parser.readRow() == false
  16. doAssert parser.row == emptySeq
  17. parser.close()
  18. strm.close()

Source Edit

  1. proc rowEntry(self: var CsvParser; entry: string): var string {.
  2. ...raises: [KeyError], tags: [], forbids: [].}

Accesses a specified entry from the current row.

Assumes that readHeaderRow has already been called.

If specified entry does not exist, raises KeyError.

Example:

  1. import std/streams
  2. var strm = newStringStream("One,Two,Three\n1,2,3\n\n10,20,30")
  3. var parser: CsvParser
  4. parser.open(strm, "tmp.csv")
  5. ## Requires calling `readHeaderRow`.
  6. parser.readHeaderRow()
  7. doAssert parser.readRow()
  8. doAssert parser.rowEntry("One") == "1"
  9. doAssert parser.rowEntry("Two") == "2"
  10. doAssert parser.rowEntry("Three") == "3"
  11. doAssertRaises(KeyError):
  12. discard parser.rowEntry("NonexistentEntry")
  13. parser.close()
  14. strm.close()

Source Edit