Какой самый быстрый способ читать текстовый файл построчно? - PullRequest
288 голосов
/ 07 ноября 2011

Я хочу читать текстовый файл построчно. Я хотел знать, насколько эффективно я делаю это в рамках .NET C #.

Это то, что я сейчас пытаюсь:

var filestream = new System.IO.FileStream(textFilePath,
                                          System.IO.FileMode.Open,
                                          System.IO.FileAccess.Read,
                                          System.IO.FileShare.ReadWrite);
var file = new System.IO.StreamReader(filestream, System.Text.Encoding.UTF8, true, 128);

while ((lineOfText = file.ReadLine()) != null)
{
    //Do something with the lineOfText
}

Ответы [ 8 ]

269 голосов
/ 07 ноября 2011

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

Использование StreamReader.ReadLine

Это в основном ваш метод.По какой-то причине вы устанавливаете размер буфера наименьшее возможное значение (128).Увеличение этого в целом увеличит производительность.Размер по умолчанию - 1024, а другие хорошие варианты - 512 (размер сектора в Windows) или 4096 (размер кластера в NTFS).Вам нужно будет запустить тест для определения оптимального размера буфера.Больший буфер - если не быстрее - по крайней мере, не медленнее, чем меньший буфер.

const Int32 BufferSize = 128;
using (var fileStream = File.OpenRead(fileName))
  using (var streamReader = new StreamReader(fileStream, Encoding.UTF8, true, BufferSize)) {
    String line;
    while ((line = streamReader.ReadLine()) != null)
      // Process line
  }

Конструктор FileStream позволяет указать FileOptions .Например, если вы читаете большой файл последовательно от начала до конца, вы можете воспользоваться FileOptions.SequentialScan.Опять же, бенчмаркинг - лучшее, что вы можете сделать.

Использование File.ReadLines

Это очень похоже на ваше собственное решение, за исключением того, что оно реализовано с использованием StreamReader с фиксированным размером буфера 1024.На моем компьютере это приводит к несколько лучшей производительности по сравнению с вашим кодом с размером буфера 128. Однако вы можете получить такое же увеличение производительности, используя больший размер буфера.Этот метод реализован с использованием блока итератора и не использует память для всех строк.

var lines = File.ReadLines(fileName);
foreach (var line in lines)
  // Process line

Использование File.ReadAllLines

Это очень похоже на предыдущий методза исключением того, что этот метод увеличивает список строк, используемых для создания возвращаемого массива строк, поэтому требования к памяти выше.Однако он возвращает String[], а не IEnumerable<String>, позволяя вам произвольно обращаться к строкам.

var lines = File.ReadAllLines(fileName);
for (var i = 0; i < lines.Length; i += 1) {
  var line = lines[i];
  // Process line
}

Использование String.Split

Этот метод значительномедленнее, по крайней мере, для больших файлов (проверено на файле размером 511 КБ), вероятно, из-за того, как реализован String.Split.Он также выделяет массив для всех строк, увеличивая требуемую память по сравнению с вашим решением.

using (var streamReader = File.OpenText(fileName)) {
  var lines = streamReader.ReadToEnd().Split("\r\n".ToCharArray(), StringSplitOptions.RemoveEmptyEntries);
  foreach (var line in lines)
    // Process line
}

Я предлагаю использовать File.ReadLines, потому что он чистый и эффективный.Если вам требуются специальные параметры обмена (например, вы используете FileShare.ReadWrite), вы можете использовать свой собственный код, но вы должны увеличить размер буфера.

194 голосов
/ 07 ноября 2011

Если вы используете .NET 4, просто используйте File.ReadLines, который сделает все за вас. Я подозреваю, что это много так же, как у вас, за исключением того, что он также может использовать FileOptions.SequentialScan и больший буфер (128 кажется очень маленьким).

31 голосов
/ 23 июля 2014

Хотя File.ReadAllLines() - это один из самых простых способов чтения файла, он также является одним из самых медленных.

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

using (StreamReader sr = File.OpenText(fileName))
{
        string s = String.Empty;
        while ((s = sr.ReadLine()) != null)
        {
               //do minimal amount of work here
        }
}

Однако, если вам приходится много делать с каждой строкой, тогда thisВ статье делается вывод, что лучшим способом является следующий (и быстрее предварительно выделить строку [], если вы знаете, сколько строк вы собираетесь прочитать):

AllLines = new string[MAX]; //only allocate memory here

using (StreamReader sr = File.OpenText(fileName))
{
        int x = 0;
        while (!sr.EndOfStream)
        {
               AllLines[x] = sr.ReadLine();
               x += 1;
        }
} //Finished. Close the file

//Now parallel process each line in the file
Parallel.For(0, AllLines.Length, x =>
{
    DoYourStuff(AllLines[x]); //do your work here
});
9 голосов
/ 11 августа 2013

Используйте следующий код:

foreach (string line in File.ReadAllLines(fileName))

Это была ОГРОМНАЯ разница в производительности чтения.

Это происходит за счет потребления памяти, но оно того стоит!

3 голосов
/ 12 августа 2013

Есть хорошая тема по этому вопросу в вопросе переполнения стека Является ли «возврат доходности» более медленным, чем возврат «старой школы»? .

Там написано:

ReadAllLines загружает все строки в память и возвращает Строка []. Все хорошо, если файл небольшой. Если файл больше, чем уместится в памяти, вам не хватит памяти.

ReadLines, с другой стороны, использует yield return для возврата одной строки в время. С его помощью вы можете прочитать файл любого размера. Это не загружает весь файл в память.

Скажем, вы хотели найти первую строку, которая содержит слово "foo", и затем выход. Используя ReadAllLines, вам нужно будет прочитать весь файл в память, даже если «foo» встречается в первой строке. С ReadLines, Вы читаете только одну строку. Какой из них будет быстрее?

3 голосов
/ 07 ноября 2011

Если размер файла не большой, то быстрее прочитать весь файл, чем разделить строку:

var filestreams = sr.ReadToEnd().Split(Environment.NewLine, 
                              StringSplitOptions.RemoveEmptyEntries);
1 голос
/ 07 ноября 2011

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

1 голос
/ 07 ноября 2011

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

...