build-a-csv-parser / lesson-02.md
Lesson 02 · A table of records and fields

Splitting a record into fields

The comma is what puts the C in CSV. Today you teach the parser to break one record into its fields on the delimiter, building each field character by character and flushing it when a comma arrives, the core loop every later feature hangs on.

The goal

Split a single record into its comma-separated fields, including empty ones.

Start here - the target
TO DO
Scenario: Fields separated by the comma delimiter
Giventhe input "a,b,c"
Whenit is parsed
Thenthe one record has three fields, ["a", "b", "c"]
Andparsing "a,,c" gives a middle empty field, ["a", "", "c"], and parsing "a,b," (a trailing comma) gives three fields with an empty last one, ["a", "b", ""]
Background

A record is not an opaque string; it is a sequence of fields joined by the delimiter, which for standard CSV is the comma. To pull the fields apart, walk the record one character at a time and accumulate a field buffer. Each time you hit a comma you have reached the end of a field, so you push whatever is in the buffer, even if it is empty, and start a fresh field. When the record ends you push the buffer one last time. That final flush is why "a,b,c" yields three fields and not two: the comma is a separator between fields, not a terminator after each one.

The empty cases are the ones people get wrong, so pin them now. Two commas in a row, "a,,c", means the middle field is the empty string, giving three fields. A record that ends in a comma, "a,b,", means the field after that last comma is empty too, giving three fields whose last one is "". A comma always separates N commas into N plus one fields, so counting the fields is the same as counting the commas and adding one. Resist the urge to reach for a library split routine: building the field buffer by hand is the exact loop the quoting state machine will grow out of.

Make it work
// build the current field in a buffer; a comma flushes it and starts the next
var field []rune
var record []string
for _, r := range input {
if r == ',' {
record = append(record, string(field)) // flush, field may be empty
field = field[:0]
} else {
field = append(field, r)
}
}
record = append(record, string(field)) // the last field after the final comma
CheckpointDONE
The parser splits a record into its comma-separated fields, empty fields included. Commit and stop here.