C#读取超大CSV文件速度过慢求助:百万行大文件处理优化
优化超大CSV文件的C#数据提取速度
你需要处理233MB、1000008行28列的CSV文件,提取第4列包含特定内容的行到DataTable,但现有OleDB和Aspose.Cells方案速度太慢,下面分析问题并给出优化方案:
现有方案的性能瓶颈
Aspose.Cells方案
当前代码会全量加载整个CSV到Workbook,再导出完整DataTable后才过滤,这会占用大量内存(加载100万行数据到内存),且后续过滤操作额外耗时,完全没必要先加载所有数据再筛选。
OleDB方案
- Jet引擎(Microsoft.Jet.OLEDB.4.0)本身对大文件处理效率较低,且仅支持32位环境;
FillSchema和writeSchema步骤会额外读取并写入Schema信息,增加不必要的开销;- DataAdapter.Fill会在内存中构建完整数据集,流式读取的效率不如直接用DataReader。
优化方案
方案1:逐行读取+实时筛选(最优选择)
直接用流式读取逐行解析CSV,仅将符合条件的行添加到DataTable,内存占用极低,速度最快,无需依赖第三方库。
使用TextFieldParser(来自Microsoft.VisualBasic.FileIO,需添加对应引用)处理CSV的分隔符、引号转义等细节:
using Microsoft.VisualBasic.FileIO; using System.Data; using System.IO; public static DataTable ReadFilteredCsv(string csvFile) { // 预先定义DataTable结构,避免自动推断列类型的开销 DataTable resultTable = new DataTable(); for (int colIndex = 0; colIndex < 28; colIndex++) { resultTable.Columns.Add($"Col{colIndex + 1}", typeof(string)); } using (TextFieldParser parser = new TextFieldParser(csvFile)) { parser.TextFieldType = FieldType.Delimited; parser.SetDelimiters(","); parser.HasFieldsEnclosedInQuotes = true; // 适配带引号包裹的CSV字段 // 若CSV包含表头,取消注释跳过第一行 // parser.ReadLine(); while (!parser.EndOfData) { try { string[] fields = parser.ReadFields(); if (fields?.Length < 4) continue; // 检查第4列(索引3)是否包含目标值 string fourthColumn = fields[3]; if (fourthColumn.Contains("27628") || fourthColumn.Contains("35627")) { resultTable.Rows.Add(fields); } } catch (MalformedLineException) { // 跳过格式错误的行,可根据需求添加日志 continue; } } } return resultTable; }
方案2:Aspose.Cells优化版本
利用Aspose.Cells的AutoFilter功能,在加载后直接筛选数据,再导出符合条件的行,避免全量导出后过滤:
using Aspose.Cells; using System.Data; public static DataTable CsvReaderAsposeOptimized(string csvFile) { DataTable dtTemp = new DataTable(); try { LoadOptions loadOptions = new LoadOptions(LoadFormat.CSV); using (Workbook workbook = new Workbook(csvFile, loadOptions)) { Worksheet worksheet = workbook.Worksheets[0]; // 对第4列(索引3)设置模糊筛选 worksheet.AutoFilter.Range = worksheet.Cells.MaxDisplayRange; worksheet.AutoFilter.AddFilter(3, "*27628*"); worksheet.AutoFilter.AddFilter(3, "*35627*"); worksheet.AutoFilter.Refresh(); // 仅导出可见行(筛选后的结果) dtTemp = worksheet.Cells.ExportDataTable(0, 0, worksheet.Cells.MaxDataRow + 1, worksheet.Cells.MaxDataColumn + 1, true); } } catch { dtTemp.Reset(); } return dtTemp; }
方案3:OleDB优化版本
替换为更高效的ACE引擎,去掉冗余操作,用DataReader流式加载数据:
using System.Data; using System.Data.OleDb; using System.IO; public static DataTable CsvReaderOleDbOptimized(string csvFile) { DataTable dtTemp = new DataTable(); try { // 使用ACE引擎(支持64位,性能优于Jet) string connString = $"Provider=Microsoft.ACE.OLEDB.12.0;Data Source={Path.GetDirectoryName(csvFile)};Extended Properties='text; HDR=NO; IMEX=1; FMT=Delimited(,)'"; string sql = $"SELECT * FROM [{Path.GetFileName(csvFile)}] WHERE F4 LIKE '%27628%' OR F4 LIKE '%35627%'"; using (OleDbConnection conn = new OleDbConnection(connString)) { conn.Open(); using (OleDbCommand cmd = new OleDbCommand(sql, conn)) { // 用DataReader流式读取,内存占用更低 using (OleDbDataReader reader = cmd.ExecuteReader()) { dtTemp.Load(reader); } } } } catch { dtTemp.Reset(); } return dtTemp; }
总结
- 优先选择逐行读取方案,内存占用仅为符合条件的行数据,速度最快,无额外依赖;
- 若必须使用Aspose.Cells或OleDB,采用对应的优化版本,避免全量加载和冗余操作;
- 注意:若CSV字段包含复杂格式(如换行、嵌套引号),可考虑使用轻量第三方库进一步简化解析逻辑。
内容的提问来源于stack exchange,提问作者Scott Guani
相关产品推荐
相关产品推荐

