betto_codec_csv

A configurable CSV codec for Dart. Decodes delimiter-separated text into a RecordSet, and encodes a RecordSet back into delimiter-separated text.

It's built around a CsvDialect rather than a fixed comma-and-quote format, so the same codec handles RFC 4180 CSV, TSV, Markdown-style pipe tables, and files with comments or boilerplate header lines to skip.

Features

  • RFC 4180 support — quoted fields, escaped quotes (""), and multi-line values, via Rfc4180Dialect / CsvDialect.rfc4180.
  • Custom dialects — configurable delimiter, quote character, "bookend" character (e.g. | for pipe tables), and whitespace trimming.
  • Header rows — infer field names from the first row, or supply a RecordSetHeader up front to assign field names and types as rows are parsed.
  • Comments — ignore full comment lines or trailing inline comments.
  • Line skipping — ignore specific line numbers (e.g. banner/metadata rows), including negative indices counted from the end of the file.
  • Round-trip encoding — render a RecordSet back to text with the same CsvDialect used to parse it.
  • Clear errors — parse failures raise CsvException with the line and column at which they occurred.

Getting started

Add the package to your pubspec.yaml (path dependency within this monorepo):

dependencies:
  betto_codec_csv: 0.1.0-dev.1

Usage

Basic decoding

import 'package:betto_codec_csv/betto_codec_csv.dart';

void main() {
  final result = Decoder().decode('a,b,c\nd,e,f');

  print(result.recordAt(0).values); // [a, b, c]
  print(result.recordAt(1).values); // [d, e, f]
}

There's also a top-level shorthand:

final result = csvDecode('a,b,c\nd,e,f');

Basic encoding

final source = Encoder().encode(result);

Or the top-level shorthand:

final source = csvEncode(result);

Header rows

Let the first row supply field names:

final csv = Decoder(dialect: CsvDialect(hasHeaderRow: true));
final result = csv.decode('first,second,third\n1,2,3');

print(result.header!.fieldNames); // [first, second, third]
print(result.first['first']!.value); // 1

Or supply a typed header up front and skip inferring it from the data:

final header = RecordSetHeader(
  schema: [
    FieldSchema.string('name'),
    FieldSchema.int('age'),
    FieldSchema.bool('active'),
  ],
);

final csv = Decoder(dialect: CsvDialect(), header: header);
final result = csv.decode('storm,28,true\nkyle,32,false');

RFC 4180 (quoted values, embedded newlines)

final csv = Decoder(dialect: Rfc4180Dialect());
final result = csv.decode('"a","b\nmulti-line","c"');

CsvCodec bundles an RFC 4180 Decoder/Encoder pair as a single Codec<RecordSet, String>, and the shared csv constant is a ready-to-use instance:

final result = csv.decoder.convert('"a","b\nmulti-line","c"');
final source = csv.encoder.convert(result);

Pipe tables

final csv = Decoder(dialect: CsvDialect(delimiter: '|', bookendChar: '|'));
final result = csv.decode('|a|b|c|');

Comments and ignored lines

final dialect = CsvDialect(
  lineCommentMarker: '#',   // lines starting with # are skipped
  ignoredLines: {1},        // also skip line 1 (e.g. a title row)
);

final csv = Decoder(dialect: dialect);

See CsvDialect for the full set of options (trimSurroundingWhitespace, ignoreEmptyLines, multiLineRecords, inlineCommentMarker, etc.), and the test/ directory for further worked examples of each.

Libraries

betto_codec_csv
A configurable CSV codec for Dart.