← 中文资源

简体中文版本

分块输入的流式 CSV 解析器

用无依赖状态机增量解析 CSV 分块,正确处理引号字段、转义引号、内嵌换行与 CRLF。

阅读英文原文

功能说明

CsvStreamParser 面向按块到达的 CSV 数据,例如文件流、网络响应或命令行管道。调用 write(chunk) 可以传入任意长度的字符串片段,解析器会跨块保留字段、行和引号状态;每完成一行就通过 onRow 回调交付一个字符串数组。它支持带引号的字段、字段内逗号、内嵌换行、双引号转义以及 CRLF 行尾。

解析器不需要一次性把整个文件读入内存,适合处理大文件。字段和行状态都保存在实例上,因此同一个实例必须按顺序写入分块。end() 用于结束输入并交付最后一行;如果引号尚未闭合,会抛出错误而不是静默产生损坏数据。

关键代码

解析器用 inQuotes 和 pendingQuote 两个状态处理引号。进入引号后,遇到引号先记为待定;下一个字符仍是引号时表示转义,追加一个引号;否则该引号是结束引号,当前字符继续按普通模式处理。

if (this.pendingQuote) {
  if (char === '"') {
    this.field += '"';
    this.pendingQuote = false;
    continue;
  }
  this.inQuotes = false;
  this.pendingQuote = false;
}

行结束时先推入最后一个字段,再重置 row 和 rowStarted 后调用回调。end() 只在确实存在待交付内容时补发最后一行,避免末尾换行产生空行。

使用注意

解析器采用严格引号规则:引号必须出现在字段开头,字段中间出现引号会抛错;引号和 CRLF 在块边界被拆开也能正确处理。onRow 抛出的异常会中断 write,调用方应决定是否捕获并继续。空行会被视为一个只含空字符串的行,这与部分 CSV 实现的行为不同;如果需要跳过空行,应在回调中过滤。

分隔符目前只支持单个字符。解析器不识别表头,也不处理 BOM,使用前应去掉 BOM。输入中的 CRLF 会被视为一个行结束;位于引号内的换行会原样保留在字段中。由于回调是同步的,慢速消费会阻塞解析,需要异步消费时应先写入队列。

验证方式

示例分两次写入同一份 CSV,第一次在字段中间截断,第二次继续,最终得到三行:表头、包含逗号和空格的引号字段,以及包含转义双引号的字段。额外的空引号字段 "" 在 end() 后应得到 [[""]],证明末尾的引号字段不会被丢弃。未闭合引号的输入会输出 unterminated quoted field,可用于确认错误分支生效。