parseRow static method

List<String>? parseRow(
  1. String input, {
  2. String fs = r',',
  3. String ts = r'"',
})

Parses single CSV row input with field separator fs and text separator ts

If the row spans multiple lines, returns null.

Throws if the row is invalid!

CsvParser.parse("Name,'Age',House");  // => [Name, Age, House]

Implementation

static List<String>? parseRow(String input,
    {String fs = r',', String ts = r'"'}) {
  final RegExp fieldWithoutTS =
      RegExp('([^$ts$fs]+)' r'(?:' + fs + r'|$)', multiLine: true);
  final RegExp fieldWithTS = RegExp(
      '$ts((?:$ts{2}|[^$ts])*)$ts' r'(?:' + fs + r'|$)',
      multiLine: true);
  final RegExp fsRegExp = RegExp(fs);
  final RegExp tsRegExp = RegExp(ts);

  final List<String> columns = [];

  while (input.isNotEmpty) {
    // Empty column
    if (input.startsWith(fsRegExp)) {
      Match? match = fsRegExp.firstMatch(input);
      if (match != null) {
        columns.add('');
        input = input.substring(match.end);
      } else {
        throw Exception('unknown error');
      }
      continue;
    }

    // Single/Multi line column without text separator
    if (input.startsWith(fieldWithoutTS)) {
      Match? match = fieldWithoutTS.firstMatch(input);
      if (match != null) {
        columns.add(match.group(1)!);
        input = input.substring(match.end);
      } else {
        throw Exception('unknown error');
      }
      continue;
    }

    // Single/Multi line column with text separator
    if (input.startsWith(fieldWithTS)) {
      Match? match = fieldWithTS.firstMatch(input);
      if (match != null) {
        columns.add(match.group(1)!);
        input = input.substring(match.end);
      } else {
        throw Exception('unknown error');
      }
      continue;
    }

    if (input.startsWith(tsRegExp)) {
      return null;
    }

    throw Exception('Invalid row!');
  }

  return columns;
}