чтение и изменение больших текстовых файлов 3-5GB

У меня есть довольно большой файл, состоящий из нескольких миллионов строк, и есть необходимость проверить и удалить поврежденные строки из файла.

Я беззастенчиво пробовал File.ReadAllLines, но это не сработало. Затем я попытался передать строки, как показано ниже, читая из исходного файла и записывая в новый. Пока он выполняет свою работу, он делает это за несколько часов (5+). Я читал об использовании буферов, что звучит как единственный вариант, но как мне таким образом сохранить целостность строки?

Решение. StreamWriter перемещен за пределы while. Вместо split используется count.

 using (FileStream inputStream = File.OpenRead((localFileToProcess + ".txt")))
 {
    using (StreamReader inputReader = new StreamReader(inputStream, System.Text.Encoding.GetEncoding(1254)))
    {
       using(StreamWriter writer=new StreamWriter(localFileToProcess,true,System.Text.Encoding.GetEncoding(1254)))
       {
          while (!inputReader.EndOfStream)
          {
             if ((tempLineValue = inputReader.ReadLine()).Count(c => c == ';') == 4)
             {
                 writer.WriteLine(tempLineValue);
             }
             else
                 incrementCounter();
          }
       }
    }
}

person mechanicum    schedule 25.07.2013    source источник
comment
Обратите внимание, что Split(';') выделяет массив и создает в памяти 5 строк для каждой строки. Это добавляет работу сборщику мусора. Может, стоит просто проверить, что в строке 4 точки с запятой? Также каждую итерацию вы создаете/удаляете StreamWriter. Не лучше ли создать его в начале и удалить в конце операции?   -  person Artemix    schedule 25.07.2013
comment
хорошая точка зрения. Я внесу изменения.   -  person mechanicum    schedule 25.07.2013
comment
как насчет чтения пакета в построитель строк, обработки и записи за один раз?   -  person bhs    schedule 25.07.2013
comment
@Artemix Попробовал ваше предложение и получил от 5+ часов до 15 минут. Просто собираюсь попробовать предложение bhs.   -  person mechanicum    schedule 25.07.2013


Ответы (1)


Я думаю, что самой медленной частью вашего исходного кода было создание/удаление StreamWriter. При каждом Dispose StreamWriter должен был сбрасывать все незаписанные данные на диск, закрывать файловые дескрипторы и т. д. В открытой ОС приходилось проверять разрешения безопасности, текущие блокировки и многое другое.

Когда вы начали использовать только один StreamWriter, его внутренний буфер записи начал работать, записывая данные на диск большими кусками. Наряду с пропуском закрытия/открытия файла для записи это экономит много времени. Дисковый ввод-вывод обычно является самой медленной частью приложения.

Split(';') также имел возможное влияние на скорость, но я думаю, что оно было менее значительным. В любом случае, операции со строками в C# следует выполнять осторожно, потому что строки являются неизменяемыми и могут создать много мусора в памяти. Итак, если вы можете проверить 4 точки с запятой, это всегда лучше, чем вызов Split(';'), который выделяет массив и (в вашем случае) создает 5 строк в памяти для каждой строки. Когда множество операций со строками выполняется с использованием неизменяемых строк, это может серьезно снизить производительность приложения даже без дискового ввода-вывода.

Что касается использования StringBuilder в вашем случае - я не думаю, что это сильно поможет, потому что StreamWriter уже имеет встроенную буферизацию.

person Artemix    schedule 25.07.2013