功能说明
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,可用于确认错误分支生效。