File handling CSV Tabular data

CSV File Handling

The klyn.io.csv package provides an RFC-4180 style reader and writer. It handles quoted fields, delimiters, embedded newlines, doubled quotes, and common dialect options.

Imports

import klyn.collections
import klyn.io
import klyn.io.csv

Reading Rows

CSVReader is a forward-only Generator<ArrayList<String>>. It reads each record on demand instead of loading the whole file. It accepts a file name, a Path, or any TextReader. File input uses bounded buffers; memory depends on the current record, not on the total number of rows.

import klyn.collections
import klyn.io
import klyn.io.csv

try reader = CSVReader("contacts.csv", delimiter=';'):
    reader.skip(1)  # Consume the header record, including any quoted newlines.

    for row as ArrayList<String> in reader:
        name = row[0]
        email = row[1]
        print(name + " <" + email + ">")

The cursor cannot be restarted. for closes it on completion, break, or an exception; try also covers errors before the loop. Owned files are closed, but a supplied TextReader remains the caller's responsibility. Quoting errors are raised when reading the affected record, not when constructing the reader. File encodings are detected from a Unicode BOM, otherwise UTF-8; pass encoding="utf-16le", for example, when needed.

A reader has no size or indexing API. For small datasets, readRows() (also named rows()) explicitly collects the remaining records into a list. It consumes and closes the cursor. Subsequent calls return an empty list. List operations apply to that result, not to the reader:

rows = CSVReader(StringReader("name\nAda\nAda")).readRows()
assert rows.size == 3ul
assert rows[1][0] == "Ada"
assert rows.indexOf(["Ada"]) == 1
assert rows.lastIndexOf(["Ada"]) == 2

Writing Rows

CSVWriter can buffer output in memory or write directly to a file or text writer. Use try with file-backed writers so the file is closed deterministically.

import klyn.io.csv

try writer = CSVWriter("contacts.csv", delimiter=';'):
    writer.writeRow(["name", "email"])
    writer.writeRow(["Ada Lovelace", "ada@example.test"])
    writer.writeRow(["Grace Hopper", "grace@example.test"])

For diagnostics or tests, create a buffered writer and call toString(). writeRows() accepts either a list or a generator such as CSVReader; generators are consumed and closed. The current writer also retains a text copy for toString() when writing to a file, so its memory usage, unlike the streaming reader's, grows with the generated output.

writer = CSVWriter(',', '"', Char(), true, "\n")
writer.writeRow(["name", "note"])
writer.writeRow(["Ada, A.", "uses \"quotes\""])
print(writer.toString())

Dialects

The reader and writer expose the usual CSV knobs: delimiter, quote character, escape character, double-quote escaping, initial-space handling, line terminator, and quoting mode.

Option Typical use
delimiter Use ';' for semicolon-separated files.
skipInitialSpace Ignore spaces immediately after delimiters.
quoting minimal, all, or none for writer output.
strict Raise CSVException on malformed quoted fields.