Kelmscott
Fixed-width records in, typed values out. Nothing in between.
Kelmscott reads and writes the fixed-width record files that instrument loggers, meter concentrators and older accounting systems still produce by the gigabyte. It does one job: turn a byte stream into typed records and back again, without guessing.
Design
Three decisions shape the whole library, and everything else follows from them.
- Layouts are declared, not inferred. A layout that is guessed from the first few records will be guessed wrongly the first time a field is blank. The layout is given up front, and the reader fails if the file disagrees with it.
- Records stream. Nothing loads a whole file into memory. A 40 GB monthly export is read in constant space, which is the case these files exist for.
- Errors carry position. Every error names the byte offset, the record number and the field. A parse failure you cannot locate in the source file is not a useful error, it is a rumour.
At a glance
layout := kelmscott.Layout{
{Name: "id", Width: 8, Type: kelmscott.Integer},
{Name: "station", Width: 12, Type: kelmscott.Text},
{Name: "taken", Width: 14, Type: kelmscott.Timestamp, Format: "20060102150405"},
{Name: "reading", Width: 10, Type: kelmscott.Decimal, Scale: 3},
}
r := kelmscott.NewReader(f, layout)
for r.Next() {
rec := r.Record()
// rec.Int("id"), rec.Text("station"), rec.Decimal("reading")
}
if err := r.Err(); err != nil {
return err
}
Integer that contains
spaces is an error, not a zero — because a zero reading and a missing reading mean
different things to whoever audits the archive four years from now.
Compatibility
| Encodings | ASCII, latin-1 and UTF-8; declared per layout, never sniffed |
|---|---|
| Line endings | LF, CRLF, or fixed-length records with no terminator at all |
| Numeric formats | Zoned decimal, packed decimal, plain ASCII with implied scale |
| Padding | Left or right, space or zero, per field |
| Stability | The layout and reader APIs have not changed since 2.0 |
What it is not
Kelmscott is not a CSV library, not a schema registry and not an ETL framework. It has no plugin system and no configuration file. If your records are delimited rather than fixed-width, the standard library already does the job better than this would.
Where to go next
The guide walks through a complete read and write cycle, including the three mistakes that account for most of the questions we get. The reference lists every type, every option and every error condition.