Обработка очень больших текстовых файлов с использованием TStreamReader и TStringList - PullRequest
0 голосов
/ 17 октября 2018

Я использую Embarcadero Rad Studio Delphi (10.2.3) и столкнулся с проблемой памяти при чтении в очень больших текстовых файлах (7 миллионов строк +, каждая строка отличается, строки могут иметь длину от 1 до 200 символов и т. Д..).Я довольно новичок в программировании на Delphi, поэтому я искал SO и Google в поисках помощи перед публикацией.

Первоначально я реализовал TStringList и прочитал файл с помощью метода LoadFromFile, но это не получилось, когда обработанные текстовые файлы сталидостаточно большой.Затем я реализовал TStreamReader и использовал ReadLn для заполнения TStringList, используя базовый код, найденный здесь:

TStringList.LoadFromFile - Исключения с большими текстовыми файлами

Пример кода:

//MyStringList.LoadFromFile(filename);
Reader := TStreamReader.Create(filename, true);
try
  MyStringList.BeginUpdate;
  try
    MyStringList.Clear;
    while not Reader.EndOfStream do
      MyStringList.Add(Reader.ReadLine);
  finally
    MyStringList.EndUpdate;
  end;
finally
  Reader.Free;
end;

Это прекрасно работало до тех пор, пока файлы, которые мне нужно было обработать, не стали большими (~ 7 миллионов строк +).Похоже, что TStringList становится настолько большим, что не хватает памяти.Я говорю «появляется», так как на самом деле у меня нет доступа к файлу, который запускается, и все данные об ошибках предоставляются моим клиентом по электронной почте, что еще более усложняет эту проблему, поскольку я не могу просто отладить ее в IDE.

Код скомпилирован 32-битным, и я не могу использовать 64-битный компилятор.Я не могу включить систему базы данных или тому подобное.К сожалению, у меня есть некоторые жесткие ограничения.Мне нужно загружать каждую строку для поиска шаблонов и сравнивать эти строки с другими строками для поиска «шаблонов в шаблонах».Я извиняюсь за то, что здесь очень расплывчато.

Суть в следующем - есть ли способ получить доступ к каждой строке в текстовом файле без использования TStringList, или, возможно, лучший способ обработки памяти TStringList?

Может быть, есть способ загрузить определенный блок строк из StreamReader в TStringList (например, прочитать в первых 100 000 строк и обработать, следующие 100 000 строк и т. Д.) Вместо всего сразу?Я думаю, что мог бы тогда написать что-нибудь, чтобы справиться с возможными «межблочными» шаблонами.

Заранее благодарен за любую помощь и предложения!

***** РЕДАКТИРОВАНИЕ С ОБНОВЛЕНИЕМ *****

Хорошо, вот базовое решение, которое мне нужно реализовать:

var
  filename: string;
  sr: TStreamReader;
  sl: TStringList;
  total, blocksize: integer;
begin
  filename := 'thefilenamegoeshere';
  sl := TStringList.Create;
  sr := TStreamReader.Create(filename, true);
  sl.Capacity := sr.BaseStream.Size div 100;
  total := 0; // Total number of lines in the file (after it is read in)
  blocksize := 10000; // The number of lines per "block"
  try
    sl.BeginUpdate;
    try
      while not sr.EndOfStream do
        begin
          sl.Clear;
          while not (sl.Count >= blocksize) do
            begin
              sl.Add(sr.ReadLine);
              total := total + 1;
              if (sr.EndOfStream = true) then break;
            end;
          // Handle the current block of lines here
        end;
    finally
      sl.EndUpdate;
    end;
  finally
    sr.Free;
    sl.Free;
  end;
end;

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

1 Ответ

0 голосов
/ 17 октября 2018

В качестве (очень) быстрого исправления вы можете попробовать использовать TALStringlist (просто замените в своем коде TStringList на TalStringList) из https://github.com/Zeus64/alcinoe. Это не очень чистый путь, но TALStringlist останется в UTF Юникода-8, уменьшив на 2 памяти используемую по умолчанию UTF 16 String.Поскольку у вас есть 7 000 000 строк около 100 символов, это означает, что около 700 МБ, это может работать на 32 битах

...