C# WinForm中如何更快将DataTable导出为CSV文件?
优化DataTable导出CSV的性能方案
你的现有代码导出速度慢的核心原因是频繁的小IO操作(逐单元格调用StreamWriter.Write),以及缺乏字符串批量处理的优化。下面是几个更高效的实现方式,同时还会修复原代码未处理的CSV格式问题(比如字段包含逗号、引号时的格式错误):
方案1:使用StringBuilder批量构建内容+一次性写入
通过StringBuilder先拼接好所有行的内容,再一次性写入文件,大幅减少IO操作次数,提升速度:
public void CreateCSVFile(DataTable dtDataTablesList, string strFilePath) { var sb = new StringBuilder(); // 写入表头 var headers = dtDataTablesList.Columns.Cast<DataColumn>() .Select(col => EscapeCsvField(col.ColumnName)); sb.AppendLine(string.Join(",", headers)); // 写入数据行 foreach (DataRow dr in dtDataTablesList.Rows) { var fields = dr.ItemArray.Select(item => { if (Convert.IsDBNull(item)) return string.Empty; return EscapeCsvField(item.ToString()); }); sb.AppendLine(string.Join(",", fields)); } // 一次性写入文件 File.WriteAllText(strFilePath, sb.ToString()); } // 处理CSV字段的特殊字符(必须的,否则导出的CSV格式会出错) private string EscapeCsvField(string field) { if (field == null) return string.Empty; // 如果字段包含逗号、引号或换行符,需要用引号包裹,内部引号转义为双引号 if (field.Contains(",") || field.Contains("\"") || field.Contains("\n") || field.Contains("\r")) { return $"\"{field.Replace("\"", "\"\"")}\""; } return field; }
方案2:直接使用File.WriteAllLines(极简高效)
File.WriteAllLines内部做了IO优化,直接生成所有行的集合后一次性写入,代码更简洁:
public void CreateCSVFile(DataTable dtDataTablesList, string strFilePath) { // 生成表头行 var headerLine = string.Join(",", dtDataTablesList.Columns.Cast<DataColumn>() .Select(col => EscapeCsvField(col.ColumnName))); // 生成数据行集合 var dataLines = dtDataTablesList.Rows.Cast<DataRow>() .Select(dr => string.Join(",", dr.ItemArray.Select(item => { if (Convert.IsDBNull(item)) return string.Empty; return EscapeCsvField(item.ToString()); }))); // 合并表头和数据行,一次性写入 File.WriteAllLines(strFilePath, new[] { headerLine }.Concat(dataLines)); } // 同样需要这个转义方法 private string EscapeCsvField(string field) { if (field == null) return string.Empty; if (field.Contains(",") || field.Contains("\"") || field.Contains("\n") || field.Contains("\r")) { return $"\"{field.Replace("\"", "\"\"")}\""; } return field; }
方案3:超大DataTable的异步处理(百万级行数)
如果你的DataTable包含几十万甚至上百万行,建议用异步写入避免阻塞主线程,同时用StreamWriter的异步方法配合批量写入:
public async Task CreateCSVFileAsync(DataTable dtDataTablesList, string strFilePath) { using var sw = new StreamWriter(strFilePath, false); // 写入表头 var headers = dtDataTablesList.Columns.Cast<DataColumn>() .Select(col => EscapeCsvField(col.ColumnName)); await sw.WriteLineAsync(string.Join(",", headers)); // 批量写入数据行(比如每1000行一次) int batchSize = 1000; var batchSb = new StringBuilder(); int count = 0; foreach (DataRow dr in dtDataTablesList.Rows) { var fields = dr.ItemArray.Select(item => { if (Convert.IsDBNull(item)) return string.Empty; return EscapeCsvField(item.ToString()); }); batchSb.AppendLine(string.Join(",", fields)); count++; if (count % batchSize == 0) { await sw.WriteAsync(batchSb.ToString()); batchSb.Clear(); } } // 写入剩余的行 if (batchSb.Length > 0) { await sw.WriteAsync(batchSb.ToString()); } } // 转义方法同上 private string EscapeCsvField(string field) { if (field == null) return string.Empty; if (field.Contains(",") || field.Contains("\"") || field.Contains("\n") || field.Contains("\r")) { return $"\"{field.Replace("\"", "\"\"")}\""; } return field; }
为什么这些方案更快?
- 减少IO操作次数:原代码每次写单个单元格,会触发大量小IO;优化后的代码要么一次性写入所有内容,要么批量写入,IO次数大幅减少。
- 字符串拼接优化:
StringBuilder比多次+拼接字符串的效率高得多,避免了大量临时字符串的创建。 - 格式正确性:新增的
EscapeCsvField方法处理了CSV的特殊字符,不仅让导出的文件格式合规,也避免了因格式错误导致的潜在解析问题。
内容的提问来源于stack exchange,提问作者user5916500
相关产品推荐
相关产品推荐

