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)at System.Text.StringBuilder.Append(Char value、Int32 repeatCount)at System.Text.StringBuilder.Append(Char value)at 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
                }
            }
          }
      }
  }

この非同期アプローチを使用すると、一度に1つのレコードのメモリのみを使用することになり、それがはるかに高速になります。

于 2013-03-05T21:52:00.610 に答える
0

これは完全な答えではありませんが、20GBのcsvファイルがある場合、リーダーがすべてをメモリに圧縮しておかない限り、すべてを一度にメモリに保存するには20GB以上が必要です(ありそうもない)。ファイルをチャンクで読み取る必要があります。大量のRAMがない場合、すべてを配列に配置するために使用しているソリューションは機能しません。

次のようなループがもう少し必要です。

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

C#のメモリ管理は、古いCSVItemを処理するときに、それらへの参照を保持しない限り、それらを破棄するのに十分なほどスマートになります。

より良いバージョンは、CSVからチャンク(たとえば10,000行)を読み取り、それらすべてを処理してから別のチャンクを取得するか、処理順序を気にしない場合はDoWhatINeedWithCsvRowのタスクを作成します。

于 2013-03-05T20:45:12.677 に答える