Как прочитать большой (1 ГБ) текстовый файл в.NET?

У меня есть текстовый файл 1 ГБ, который мне нужно читать построчно. Какой самый лучший и быстрый способ сделать это?

private void ReadTxtFile()
{            
    string filePath = string.Empty;
    filePath = openFileDialog1.FileName;
    if (string.IsNullOrEmpty(filePath))
    {
        using (StreamReader sr = new StreamReader(filePath))
        {
            String line;
            while ((line = sr.ReadLine()) != null)
            {
                FormatData(line);                        
            }
        }
    }
}

В FormatData() Я проверяю начальное слово строки, которое должно быть сопоставлено со словом и на основе этого приращения является целочисленной переменной.

void FormatData(string line)
{
    if (line.StartWith(word))
    {
        globalIntVariable++;
    }
}

9 ответов

Решение

Если вы используете.NET 4.0, попробуйте MemoryMappedFile, который предназначен для этого сценария.

Ты можешь использовать StreamReader.ReadLine иначе.

Использование StreamReader, вероятно, является способом, поскольку вы не хотите, чтобы весь файл находился в памяти сразу. MemoryMappedFile - это больше для произвольного доступа, чем для последовательного чтения (для последовательного чтения он в десять раз быстрее, а для оперативного отображения в десять раз быстрее).

Вы также можете попробовать создать потоковый ридер из файлового потока с FileOptions, установленным на SequentialScan (см. Перечисление FileOptions), но я сомневаюсь, что это будет иметь большое значение.

Однако есть способы сделать ваш пример более эффективным, поскольку вы выполняете форматирование в том же цикле, что и чтение. Вы тратите впустую такты, поэтому, если вы хотите еще большей производительности, было бы лучше с многопоточным асинхронным решением, где один поток считывает данные, а другой форматирует их, когда они становятся доступными. Оформить заказ BlockingColletion, который может соответствовать вашим потребностям:

Блокировка коллекции и проблема производителя-потребителя

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

Вы можете использовать LINQ:

int result = File.ReadLines(filePath).Count(line => line.StartsWith(word));

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

Enumerable.Count считает строки, начинающиеся со слова.

Если вы вызываете это из потока пользовательского интерфейса, используйте BackgroundWorker.

Вероятно, чтобы прочитать это построчно.

Вы не должны пытаться принудительно ввести его в память, читая до конца, а затем обрабатывая.

StreamReader.ReadLine должно работать нормально. Позвольте фреймворку выбирать буферизацию, если вы не знаете, с помощью профилирования вы можете сделать лучше

TextReader.ReadLine()

Я столкнулся с той же проблемой на нашем производственном сервере в Агенте, где мы видим большие файлы (иногда файлы TXT с разделителями табуляции 10-25 ГБ (\t)). И после многих испытаний и исследований я нашел лучший способ чтения больших файлов небольшими порциями с циклом for / foreach и настройкой логики смещения и ограничения с помощью File.ReadLines().

int TotalRows = File.ReadLines(Path).Count(); // Count the number of rows in file with lazy load
int Limit = 100000; // 100000 rows per batch
for (int Offset = 0; Offset < TotalRows; Offset += Limit)
{
  var table = Path.FileToTable(heading: true, delimiter: '\t', offset : Offset, limit: Limit);

 // Do all your processing here and with limit and offset and save to drive in append mode
 // The append mode will write the output in same file for each processed batch.

  table.TableToFile(@"C:\output.txt");
}

Смотрите полный код в моей библиотеке Github: https://github.com/Agenty/FileReader/

Полное раскрытие - я работаю на Agenty, компанию, которая владела этой библиотекой и веб-сайтом

Мой файл превышает 13 ГБ:

введите описание изображения здесь

Вы можете использовать мой класс:

public static void Read(int length)
    {
        StringBuilder resultAsString = new StringBuilder();

        using (MemoryMappedFile memoryMappedFile = MemoryMappedFile.CreateFromFile(@"D:\_Profession\Projects\Parto\HotelDataManagement\_Document\Expedia_Rapid.jsonl\Expedia_Rapi.json"))
        using (MemoryMappedViewStream memoryMappedViewStream = memoryMappedFile.CreateViewStream(0, length))
        {
            for (int i = 0; i < length; i++)
            {
                //Reads a byte from a stream and advances the position within the stream by one byte, or returns -1 if at the end of the stream.
                int result = memoryMappedViewStream.ReadByte();

                if (result == -1)
                {
                    break;
                }

                char letter = (char)result;

                resultAsString.Append(letter);
            }
        }
    }

Этот код будет читать текст файла от начала до длины, передаваемой методу Read(int length), и заполнять переменную resultAsString.

Он вернет нижний текст:

Я бы прочитал файл по 10000 байт за раз. Затем я проанализировал бы эти 10000 байтов, разделил их на строки и передал в функцию FormatData.

Бонусные баллы за разделение чтения и анализа строк на несколько потоков.

Я бы определенно использовал StringBuilder для сбора всех строк и мог бы создать строковый буфер, чтобы постоянно хранить в памяти около 100 строк.

Другие вопросы по тегам