You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

StringReader.ReadLine逐行读取CSV文件出现内容截断如何解决?

问题根因

  1. CSV字段内部合法换行被误切割
    CSV规范允许被双引号包裹的字段内部包含换行符,这类换行属于字段内容的一部分,不是记录的分隔边界。你直接调用ReadLine()是按物理换行切割内容,会把单条逻辑记录拆成多个片段,最终导致记录解析不完整、内容看起来被截断。这也正好对应你部分文件正常、部分文件异常的现象:没有内部换行的CSV可以正常读取,带内部换行的CSV就会出问题。
  2. 逐行拼接逻辑丢失换行标记
    ReadLine()返回的结果会自动剥离行尾的\r、\n或\r\n,你直接用+=拼接的时候没有补回换行符,最终拼接出来的字符串和原内容结构不一致,也会造成内容被截断的错觉。
  3. 普通文本读取逻辑不适用于CSV格式
    你参考的官方示例是针对无特殊转义规则的普通文本,默认每行对应一条独立记录。而CSV有专属的转义规则,不能直接套用普通文本的逐行读取逻辑。

兼容逐行处理的解决方案

方案1:使用成熟CSV解析库(最推荐)

不要手动处理CSV的边界判断逻辑,直接用业内久经验证的解析库,内置支持带内部换行的字段解析,同时天然支持逐行处理逻辑记录,不需要自己处理底层读取细节,稳定性和兼容性都远高于自行实现的逻辑。

方案2:自行实现支持CSV转义规则的逐行读取

如果必须自己实现读取逻辑,可以参考如下思路处理:
每次读取物理行后,统计当前未闭合的双引号数量,如果为奇数,说明当前物理行属于上一条逻辑记录的一部分,需要补回换行符后和后续内容拼接,直到双引号数量为偶数,才代表一条完整的逻辑记录读取完成。
示例代码如下:

StringBuilder currentRecord = new StringBuilder();
string line;
// 直接用ReadLine返回值判空即可,无需额外调用Peek
while ((line = stringReader.ReadLine()) != null)
{
    currentRecord.Append(line);
    // 统计未转义的双引号数量(CSV中""代表转义的单个双引号)
    string temp = currentRecord.ToString().Replace("\"\"", string.Empty);
    int unescapedQuoteCount = temp.Count(c => c == '"');
    
    if (unescapedQuoteCount % 2 == 0)
    {
        // 完整逻辑记录读取完成,执行你的业务处理逻辑
        ProcessRecord(currentRecord.ToString());
        currentRecord.Clear();
    }
    else
    {
        // 属于字段内部换行,补回被ReadLine移除的换行符
        currentRecord.Append(Environment.NewLine);
    }
}

方案3:确认无内部换行场景的简化方案

如果你能确认所有待处理的CSV都不存在引号包裹的内部换行,只是原有逻辑有缺陷,可以直接修正为如下写法:

StringBuilder resultBuilder = new StringBuilder();
string line;
while ((line = stringReader.ReadLine()) != null)
{
    // 此处执行你的逐行业务处理逻辑
    ProcessLine(line);
    // 如果需要拼接完整内容,调用AppendLine补回换行
    resultBuilder.AppendLine(line);
}
string results = resultBuilder.ToString();

相较于你原来的写法,该方案用StringBuilder替代字符串直接拼接提升性能,同时补回了行尾换行符,用ReadLine返回值判空的逻辑也比额外调用Peek更简洁可靠。

内容的提问来源于stack exchange,提问作者Captain

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.07 16:42:00