Форум по Delphi программированию

Delphi Sources



Вернуться   Форум по Delphi программированию > Все о Delphi > [ "Начинающим" ]
Ник
Пароль
Регистрация <<         Правила форума         >> FAQ Пользователи Календарь Поиск Сообщения за сегодня Все разделы прочитаны

Ответ
 
Опции темы Поиск в этой теме Опции просмотра
  #1  
Старый 23.09.2018, 16:41
Taras2020 Taras2020 вне форума
Прохожий
 
Регистрация: 15.01.2018
Сообщения: 36
Версия Delphi: Delphi 7
Репутация: 10
По умолчанию Сравнение текстовых файлов \ Варианты

Добрый день, Уважаемые форумчане.

У меня возник такой вопрос: Строки текстового файла 1.txt необходимо проверить на совпадение - со строками текстового файла 2.txt. И уникальные строки, которых нет в файле 1.txt, записать в файл 3.txt.

К примеру:

Tекст в первом файле - 1.txt
Test1
Test2
Test3
Test4
Test5

Текст во втором файле - 2.txt
Test1
Test2
Test3
Test4
Test5
Stroka1
Stroka2
stroka3

После сравнения, результат в третьем файле - 3.txt
Stroka1
Stroka2
stroka3

Вот мои решения и их недостатки =>

Вариант №1 (Загрузка в память):
Код:
var
  i: integer;
  s1, s2: TStrings;
begin
  s1 := TStringList.Create;
  s2 := TStringList.Create;
  Try
    s1.LoadFromFile('1.txt');
    s2.LoadFromFile('2.txt');
    for i := s1.Count - 1 downto 0 do
      if s2.IndexOf(s1[i]) >= 0 Then
        s1.Delete(i);
    s1.SaveToFile('3.txt');
  Finally
    s1.Free;
    s2.Free;
  End;
end;

Проблема в следующем: Если файл размеров 500 мегабайт то вся память забивается и выскакивает ошибка - Out of memory. Ну это само собой понятно поскольку я загружаю и обрабатываю все в памяти.

Вопрос: Может как то можно оптимизировать код в этом варианте, кто что может подсказать ?

Вариант №2 (Чтение построчно):
Код:
var
  f1, f2, f3: textfile;
  s1, s2: string;
  b: boolean;
begin
  assignfile(f1,  '1.txt');
  assignfile(f2,  '2.txt');
  assignfile(f3, '3.txt');
  rewrite(f3);
  reset(f1);
  while not eof(f1) do
  begin
    readln(f1, s1);
    reset(f2);
    b := true;
    while not eof(f2) and b do
    begin
      readln(f2, s2);
      if s2 = s1 then
        b := false;
    end;
    if b then
      writeln(f3, s1);
  end;
  closefile(f1);
  closefile(f2);
  closefile(f3);
end;

Проблема в следующем: Очень и очень медленно работает, ну и тут принцип понятен:
1.Читаем строку из 1.txt файла
2.Открываем 2.txt, проходим по нему ищем совпадение
3.Закрываем 2.txt файл
И так для каждой строки из первого файла. Это куча времени и затрат.

Вопрос, скорее всего утверждение: В этом варианте, само собой понятно что далеко не уедешь.

П.С: Подскажите еще варианты, при использования которых, можно обрабатывать файлы до 1 гигабайта, не загружая память и при этом, иметь, хотя бы, среднюю скорость обработки ?. Заранее благодарен за помощь...
Ответить с цитированием
  #2  
Старый 23.09.2018, 19:45
lmikle lmikle вне форума
Модератор
 
Регистрация: 17.04.2008
Сообщения: 8,057
Версия Delphi: 7, XE3, 10.2
Репутация: 49089
По умолчанию

Вариант 1, гибридный. Работает, если файл образцов (т.е. 1.txt) не очень большой. Грузишь 1.txt в память. Другой файл читаешь построчно и проверяешь против списка строк в памяти.

Вариант 2. Считаешь хэши для строк в обоих файлах. Причем для файла 2.txt сохраняешь и сам хэш, и позицию строки. Сравниваешь хэши, для неотсеяных хэшей потом по позиции строки переписываешь нужные строки в выходной файл.

Кстати, примерный расчет по памяти в случае второго варианта.
Пусть средняя длинна строки будет 40 байт. Размер хэша - 128 бит, т.е. 16 байт. Плюс 32 бита (4 байта) на позицию строки. Т.о. получаем 20 байт. Т.е. сокращение объема данных в 2 раза. Если средняя длинна строки больше, то и выигрыш по пямяти больше.

ЗЫ. Кстати, если грузить в память только один файл, то, как мне кажется, вполне можно уместиться. Пусть у нас 32-битное приложение, т.е. мы можем адресовать 2Гб памяти (вариант расширенной адресации до 3Гб не рассматриваем, хотя он тоже есть). На загрузку одного из файлов уйдет 1Гб, на маппинг системы и самой программы уйдет, ну пусть по макс, еще где-то 0.5Гб. Соответсвенно, у тебя остается около 0.5Гб для данных из второго файла.

ЗЗЫ. Кстати, читать файл, который фильтруешь, можно и блоками. Допустим, читаешь блок из 1000 строк, проверяешь их, потом читаешь следующий блок.

ЗЗЗЫ. Сорри, сумбурно немного. Просто накидывал идеи, все идеи надо проверять против реальных данных, какая эвристика даст лучший результат.
Ответить с цитированием
Этот пользователь сказал Спасибо lmikle за это полезное сообщение:
Taras2020 (24.09.2018)
  #3  
Старый 24.09.2018, 08:38
Taras2020 Taras2020 вне форума
Прохожий
 
Регистрация: 15.01.2018
Сообщения: 36
Версия Delphi: Delphi 7
Репутация: 10
По умолчанию

Цитата:
Сообщение от lmikle
ЗЗЫ. Кстати, читать файл, который фильтруешь, можно и блоками. Допустим, читаешь блок из 1000 строк, проверяешь их, потом читаешь следующий блок.
Отличные советы. Интересный вариант про чтение блоками, но вот как его реализовать, нет опыта . Хотя бы приблизительно, наброску кода можете сделать. Буду премного благодарен....
Ответить с цитированием
  #4  
Старый 24.09.2018, 11:32
lmikle lmikle вне форума
Модератор
 
Регистрация: 17.04.2008
Сообщения: 8,057
Версия Delphi: 7, XE3, 10.2
Репутация: 49089
По умолчанию

Ну так и читать. Собственно, тут и делать ничего на самом деле не надо.
Просто окрываешь файл через стандартные средства паскаля (AssignFile/Reset/CloseFile), Библиотека сама закеширует.
Если же хочется самому контролировать процесс, то можно примерно так:
1. Открываем файл через TFileStream.
2. Читаем блок данных определенного размера в байтах.
3. Ищем ближайший конец строки (#13#10) с конца файла.
4. Смещаем указатель в потоке на найденную дистанцию (Stream.seek - параметры посмотри в справке - тебе нужно от текущей позиции назад).
5. Грузми прочитанный блок в TStringList или что-то подобное и начинаем обработку полученного блока.

ЗЫ. Код лень ваять в 3 часа ночи...
Ответить с цитированием
Этот пользователь сказал Спасибо lmikle за это полезное сообщение:
Taras2020 (24.09.2018)
  #5  
Старый 24.09.2018, 13:36
Taras2020 Taras2020 вне форума
Прохожий
 
Регистрация: 15.01.2018
Сообщения: 36
Версия Delphi: Delphi 7
Репутация: 10
По умолчанию

Цитата:
Сообщение от lmikle
Ну так и читать. Собственно, тут и делать ничего на самом деле не надо.
Просто окрываешь файл через стандартные средства паскаля (AssignFile/Reset/CloseFile), Библиотека сама закеширует.
Если же хочется самому контролировать процесс, то можно примерно так:
1. Открываем файл через TFileStream.
2. Читаем блок данных определенного размера в байтах.
3. Ищем ближайший конец строки (#13#10) с конца файла.
4. Смещаем указатель в потоке на найденную дистанцию (Stream.seek - параметры посмотри в справке - тебе нужно от текущей позиции назад).
5. Грузми прочитанный блок в TStringList или что-то подобное и начинаем обработку полученного блока.

ЗЫ. Код лень ваять в 3 часа ночи...
Лады, попробую разобраться. По возможности, как будет время, подкиньте пример. В любом случае спасибо!
Ответить с цитированием
  #6  
Старый 24.09.2018, 17:52
Аватар для Страдалецъ
Страдалецъ Страдалецъ вне форума
Гуру
 
Регистрация: 09.03.2009
Адрес: На курорте, из окна вижу теплое Баренцево море. Бррр.
Сообщения: 4,723
Репутация: 52347
По умолчанию

А вы уверены что вам нужен этот велосипед? Существуют отличные готовые решения. Может имеет смысл воспользоваться готовым? Например можно попробовать Beyong Compare.
__________________
Жизнь такова какова она есть и больше никакова.
Помогаю за спасибо.
Ответить с цитированием
Этот пользователь сказал Спасибо Страдалецъ за это полезное сообщение:
Taras2020 (24.09.2018)
  #7  
Старый 24.09.2018, 19:34
lmikle lmikle вне форума
Модератор
 
Регистрация: 17.04.2008
Сообщения: 8,057
Версия Delphi: 7, XE3, 10.2
Репутация: 49089
По умолчанию

Да что уж там, можно и обычным юниксовым diff'ом сделать при желании.
Вот только мы ж не значем зачем и для чего это конкретно нужно... Если это какой-то средний шаг в обработке данных, то не совсем удобно использовать внешнее решение, если можно сделать самому.
Ответить с цитированием
Этот пользователь сказал Спасибо lmikle за это полезное сообщение:
Taras2020 (24.09.2018)
  #8  
Старый 24.09.2018, 21:59
Taras2020 Taras2020 вне форума
Прохожий
 
Регистрация: 15.01.2018
Сообщения: 36
Версия Delphi: Delphi 7
Репутация: 10
По умолчанию

Цитата:
Сообщение от lmikle
Да что уж там, можно и обычным юниксовым diff'ом сделать при желании.
Вот только мы ж не значем зачем и для чего это конкретно нужно... Если это какой-то средний, то не совсем удобно использовать внешнее решение, если можно сделать самому.
Читаете мои мысли, да это шаг в обработке данных. Я бы так, давно, уже готовую программу использовал, их полно в сети. Ну ничего, я настойчив и попробую разобраться. В любом случае спасибо за наставление!
Ответить с цитированием
Ответ


Delphi Sources

Опции темы Поиск в этой теме
Поиск в этой теме:

Расширенный поиск
Опции просмотра

Ваши права в разделе
Вы не можете создавать темы
Вы не можете отвечать на сообщения
Вы не можете прикреплять файлы
Вы не можете редактировать сообщения

BB-коды Вкл.
Смайлы Вкл.
[IMG] код Вкл.
HTML код Выкл.
Быстрый переход


Часовой пояс GMT +3, время: 07:25.


 

Сайт

Форум

FAQ

RSS лента

Прочее

 

Copyright © Форум "Delphi Sources" by BrokenByte Software, 2004-2023

ВКонтакте   Facebook   Twitter