FileHelpers выдает исключение OutOfMemoryException при разборе большого файла CSV

Я пытаюсь разобрать очень большой CSV-файл с помощью FileHelpers (http://www.filehelpers.net/ ). Размер файла составляет 1 ГБ в архиве и около 20 ГБ в разархивированном виде.

        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, Int32 repeatCount) в System.Text.StringBuilder.Append (значение Char) в FileHelpers.StringHelperoted.LineString quoteChar, Boolean allowMultiline) в FileHelpers.DelimitedField.ExtractFieldString (строка LineInfo) в FileHelpers.FieldBase.ExtractValue (строка LineInfo) в FileHelpers.RecordInfo.StringToRecord (строка LineInfo) в FileHeladerEngreamReader_2

Можно ли проанализировать такой большой файл с помощью FileHelpers? Если нет, может ли кто-нибудь порекомендовать подход к синтаксическому анализу файлов такого размера? Спасибо.


person BowserKingKoopa    schedule 05.03.2013    source источник
comment
BowserKingKoopa, мой первый вопрос был бы очевиден: сколько свободного места у вас есть при распаковке файла, если это 20 ГБ, я бы удвоил это, чтобы увидеть, есть ли у вас 40 ГБ свободного места   -  person MethodMan    schedule 06.03.2013
comment
Вы хотите поместить в оперативную память ~ 20 ГБ данных? Действительно ?   -  person digEmAll    schedule 06.03.2013
comment
Разве вам не следует использовать BinaryReader вместо TextReader? FileHelpers обрабатывает размер буфера, или вам нужно установить его самостоятельно ..?   -  person MethodMan    schedule 06.03.2013
comment
На мой взгляд, это то, что вы должны обрабатывать с базой данных (так что фактические данные останутся на HD) ... почему, например, вы не создаете базу данных sqlite и не импортируете этот CSV в таблицу?   -  person digEmAll    schedule 06.03.2013


Ответы (2)


Вы должны работать запись за записью следующим образом:

  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
                }
            }
          }
      }
  }

Если вы используете этот асинхронный подход, вы будете использовать память только для записи один раз, и это будет намного быстрее.

person Marcos Meli    schedule 05.03.2013
comment
Спасибо! FileHelperAsyncEngine - это именно то, что я искал. - person BowserKingKoopa; 06.03.2013

Это не полный ответ, но если у вас есть файл csv объемом 20 ГБ, вам понадобится 20 ГБ + для одновременного хранения всего этого в памяти, если ваш читатель не держит все сжатым в памяти (маловероятно). Вам нужно читать файл по частям, и решение, которое вы используете для помещения всего в массив, не будет работать, если у вас нет огромного количества оперативной памяти.

Вам нужен цикл, похожий на этот:

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

Тогда управление памятью C # будет достаточно умным, чтобы избавляться от старых CSVItems, когда вы просматриваете их, при условии, что вы не сохраняете ссылки на них повсюду.

Лучшая версия могла бы прочитать фрагмент из CSV (например, 10 000 строк), обработать все это, затем получить другой фрагмент или создать задачу для DoWhatINeedWithCsvRow, если вас не волнует порядок обработки.

person sammy_winter    schedule 05.03.2013