C#实现xlsx转csv时字段含逗号被错误拆分的问题
问题描述
我正尝试通过如下代码实现xlsx文件转csv功能:
public static bool saveAsCsv(string excelFilePath, string destinationCsvPath) { Encoding.RegisterProvider(CodePagesEncodingProvider.Instance); using (var stream = new FileStream(excelFilePath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite)) { IExcelDataReader? reader = null; if (excelFilePath.EndsWith(".xls")) { reader = ExcelReaderFactory.CreateBinaryReader(stream); } else if (excelFilePath.EndsWith(".xlsx")) { reader = ExcelReaderFactory.CreateOpenXmlReader(stream); } if (reader == null) return false; var ds = reader.AsDataSet(new ExcelDataSetConfiguration() { ConfigureDataTable = (tableReader) => new ExcelDataTableConfiguration() { UseHeaderRow = false } }); var csvContent = string.Empty; int row_no = 0; while (row_no < ds.Tables[0].Rows.Count) { var arr = new List<string>(); for (int i = 0; i < ds.Tables[0].Columns.Count; i++) { #pragma warning disable CS8604 // Possible null reference argument. arr.Add(ds.Tables[0].Rows[row_no][i].ToString()); #pragma warning restore CS8604 // Possible null reference argument. } row_no++; csvContent += string.Join(",", arr) + "\n"; } StreamWriter csv = new StreamWriter(destinationCsvPath, false); csv.Write(csvContent); csv.Close(); return true; } }
上述代码可正常运行,但存在一个问题:当字段内容本身包含逗号分隔的两个值时,会被识别为两个独立字段。
举例如下:
本应处于同一个字段内的内容:
Test A, test B
转换后该内容被拆分到了不同字段中,需要修改代码避免该问题。
问题根因
当前实现没有遵循标准CSV格式的转义规则:
- 若字段内容包含逗号、换行符、半角双引号,必须用半角双引号将整个字段包裹
- 若字段本身包含半角双引号,需要将单个双引号替换为两个连续双引号做转义
直接用逗号拼接未转义的原始字段值,遇到内容自带逗号的场景自然会被CSV解析器识别为字段分隔符,造成拆分错误。
修复方案
方案1:补充标准CSV转义逻辑(无需新增依赖)
先实现专门的字段转义方法处理所有特殊字符场景,同时优化原代码的资源释放、字符串拼接效率、编码指定问题,修改后的完整代码如下:
/// <summary> /// 按CSV标准规则转义单个字段 /// </summary> private static string EscapeCsvField(string fieldValue) { if (string.IsNullOrEmpty(fieldValue)) return string.Empty; // 存在特殊字符时,用双引号包裹字段,同时转义字段内的双引号 if (fieldValue.Contains(',') || fieldValue.Contains('"') || fieldValue.Contains('\n') || fieldValue.Contains('\r')) { return $"\"{fieldValue.Replace("\"", "\"\"")}\""; } return fieldValue; } public static bool saveAsCsv(string excelFilePath, string destinationCsvPath) { Encoding.RegisterProvider(CodePagesEncodingProvider.Instance); using (var stream = new FileStream(excelFilePath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite)) { IExcelDataReader? reader = null; if (excelFilePath.EndsWith(".xls")) { reader = ExcelReaderFactory.CreateBinaryReader(stream); } else if (excelFilePath.EndsWith(".xlsx")) { reader = ExcelReaderFactory.CreateOpenXmlReader(stream); } if (reader == null) return false; using (reader) { var ds = reader.AsDataSet(new ExcelDataSetConfiguration() { ConfigureDataTable = (tableReader) => new ExcelDataTableConfiguration() { UseHeaderRow = false } }); var dataTable = ds.Tables[0]; // 用StringBuilder代替字符串累加,提升大文件处理效率 var csvBuilder = new StringBuilder(); for (int rowNo = 0; rowNo < dataTable.Rows.Count; rowNo++) { var rowFields = new List<string>(); for (int colNo = 0; colNo < dataTable.Columns.Count; colNo++) { string fieldValue = dataTable.Rows[rowNo][colNo]?.ToString() ?? string.Empty; // 对每个字段做转义后再加入行数据 rowFields.Add(EscapeCsvField(fieldValue)); } csvBuilder.AppendLine(string.Join(",", rowFields)); } // 指定UTF-8编码写入,避免Excel打开乱码,用using自动释放资源 using (var csvWriter = new StreamWriter(destinationCsvPath, false, Encoding.UTF8)) { csvWriter.Write(csvBuilder.ToString()); } return true; } } }
方案2:使用成熟CSV库(适合复杂场景)
如果后续需要处理更多CSV边缘场景(比如不同分隔符、自定义转义规则、超大文件流式写入),可以直接引入专门的CSV处理库代替手动拼接,从根源上避免转义逻辑写漏的问题。
注意事项
- 所有字段都建议走转义逻辑,不要只判断当前已知的特殊字符场景,避免后续数据变化出现兼容问题
- 写入文件时明确指定编码,优先使用带BOM的UTF-8,避免Windows下用Excel打开CSV出现中文乱码
- 文件流、DataReader、写入器这类资源都要放在
using块中,自动完成资源释放,避免文件占用、内存泄漏问题 - 大文件转换场景不要把全部内容拼接到内存字符串中,建议逐行读取逐行写入,降低内存占用
内容的提问来源于stack exchange,提问作者lross15
相关产品推荐
相关产品推荐

