7

我正在尝试使用 FileHelpers ( http://www.filehelpers.net/ ) 解析一个非常大的 csv 文件。该文件是 1GB 的压缩文件和大约 20GB 的解压缩文件。

        string fileName = @"c:\myfile.csv.gz";
        using (var fileStream = File.OpenRead(fileName))
        {
            using (GZipStream gzipStream = new GZipStream(fileStream, CompressionMode.Decompress, false))
            {
                using (TextReader textReader = new StreamReader(gzipStream))
                {
                    var engine = new FileHelperEngine<CSVItem>();
                    CSVItem[] items = engine.ReadStream(textReader);                        
                }
            }
        }

FileHelpers 然后抛出 OutOfMemoryException。

测试失败:引发了“System.OutOfMemoryException”类型的异常。System.OutOfMemoryException:引发了“System.OutOfMemoryException”类型的异常。在 System.Text.StringBuilder.ExpandByABlock(Int32 minBlockCharCount) 在 System.Text.StringBuilder.Append(Char value, Int32 repeatCount) 在 System.Text.StringBuilder.Append(Char value) 在 FileHelpers.StringHelper.ExtractQuotedString(LineInfo line, Char quoteChar, Boolean allowMultiline) at FileHelpers.DelimitedField.ExtractFieldString(LineInfo line) at FileHelpers.FieldBase.ExtractValue(LineInfo line) at FileHelpers.RecordInfo.StringToRecord(LineInfo line) at FileHelpers.FileHelperEngine 1.ReadStream(TextReader reader, Int32 maxRecords, DataTable dt) at FileHelpers.FileHelperEngine1.ReadStream(TextReader reader)

是否可以使用 FileHelpers 解析这么大的文件?如果没有,任何人都可以推荐一种解析这么大文件的方法吗?谢谢。

4

2 回答 2

13

您必须以这种方式逐条记录:

  string fileName = @"c:\myfile.csv.gz";
  using (var fileStream = File.OpenRead(fileName))
  {
      using (GZipStream gzipStream = new GZipStream(fileStream, CompressionMode.Decompress, false))
      {
          using (TextReader textReader = new StreamReader(gzipStream))
          {
            var engine = new FileHelperAsyncEngine<CSVItem>();
            using(engine.BeginReadStream(textReader))
            {
                foreach(var record in engine)
                {
                   // Work with each item
                }
            }
          }
      }
  }

如果您使用这种异步方法,您一次只会使用内存记录一次,而且速度会更快。

于 2013-03-05T21:52:00.610 回答
0

这不是一个完整的答案,但如果您有一个 20GB 的 csv 文件,则需要 20GB+ 才能一次将整个内容存储在内存中,除非您的阅读器将所有内容都压缩在内存中(不太可能)。您需要分块读取文件,如果您没有大量内存,您使用的将所有内容放入数组的解决方案将不起作用。

你需要一个更像这样的循环:

CsvReader reader = new CsvReader(filePath)
CSVItem item = reader.ReadNextItem();
while(item != null){
  DoWhatINeedWithCsvRow(item);
  item = reader.ReadNextItem();
}

C# 的内存管理将足够智能,可以在您浏览旧的 CSVItems 时将其处理掉,前提是您不保留对它们的引用。

更好的版本会从 CSV 读取一个块(例如 10,000 行),处理所有这些,然后获取另一个块,或者如果您不关心处理顺序,则为 DoWhatINeedWithCsvRow 创建一个任务。

于 2013-03-05T20:45:12.677 回答