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

У меня есть два текстовых файла по 3 ГБ, в каждом файле около 80 миллионов строк. И они имеют 99,9% одинаковых строк (в файле А 60 000 уникальных строк, в файле Б 80 000 уникальных строк).

Как я могу быстро найти эти уникальные строки в двух файлах? Есть ли для этого готовые инструменты командной строки? Я использую Python, но полагаю, что найти эффективный метод Pythonic для загрузки файлов и сравнения будет сложнее.

Любые предложения приветствуются.


person jack    schedule 23.08.2010    source источник
comment
Вы имеете в виду, что 99,9% файлов идентичны или что 99,9% строк идентичны (т.е. одна и та же строка повторяется)?   -  person bstpierre    schedule 23.08.2010
comment
Вам важен порядок строк? Есть ли в B все строки A в том же порядке, что и A? Может ли быть переупорядочение, удаление строк? Есть ли повторяющиеся строки, количество которых имеет значение (A имеет n раз, B имеет n-b раз-> разница составляет b*строку)   -  person Tony Veijalainen    schedule 23.08.2010
comment
Если вы спросите о готовых к использованию инструментах командной строки, вы можете указать ОС. В большинстве случаев diff является родным или портированным. Тем не менее, я не могу быть уверен, что вы хотите от своего вопроса: возможно, в Linux: sort --unique ‹ file1 › uniq1; sort --unique ‹ file2 › uniq1; diff uniq[12].   -  person Tony Delroy    schedule 23.08.2010
comment
Сколько байтов в строке в среднем?   -  person Daniel Stutzbach    schedule 23.08.2010
comment
@bstpierre, точно, 99,9% строк в двух файлах идентичны, но уникальные строки случайным образом разбросаны по двум файлам.   -  person jack    schedule 23.08.2010
comment
@Tony Veijalainen, все строки в случайном порядке. Я просто надеюсь найти все уникальные строки в файле A и все уникальные строки в файле B, любой порядок в порядке. В обоих файлах нет повторяющихся строк.   -  person jack    schedule 23.08.2010
comment
@Daniel Stutzbach, средняя длина строки составляет 35 байт.   -  person jack    schedule 23.08.2010


Ответы (5)


Если порядок имеет значение, попробуйте утилиту comm. Если порядок не имеет значения, sort file1 file2 | uniq -u.

person zwol    schedule 23.08.2010
comment
Как сортировка двух файлов 3G будет быстрее, чем diff? - person bstpierre; 23.08.2010
comment
@bstpierre: реализация diff обычно квадратична, а сортировка обычно n log n в среднем случае (быстрая сортировка). - person tonfa; 23.08.2010

Я думаю, что это самый быстрый метод (будь то на Python или на другом языке, не должно иметь большого значения, IMO).

Примечания:

1. Я сохраняю хеш каждой строки только для экономии места (и времени, если может произойти пейджинг)

2. Из-за вышеизложенного я распечатываю только номера строк; если вам нужны настоящие строки, вам просто нужно снова прочитать файлы

3. Я предполагаю, что хеш-функция не приводит к конфликтам. Это почти, но не совершенно точно.

4. Я импортирую hashlib, потому что встроенная функция hash() слишком короткая, чтобы избежать конфликтов.

import sys
import hashlib

file = []
lines = []
for i in range(2):
    # open the files named in the command line
    file.append(open(sys.argv[1+i], 'r'))
    # stores the hash value and the line number for each line in file i
    lines.append({})
    # assuming you like counting lines starting with 1
    counter = 1
    while 1:
        # assuming default encoding is sufficient to handle the input file
        line = file[i].readline().encode()
        if not line: break
        hashcode = hashlib.sha512(line).hexdigest()
        lines[i][hashcode] = sys.argv[1+i]+': '+str(counter)
        counter += 1
unique0 = lines[0].keys() - lines[1].keys()
unique1 = lines[1].keys() - lines[0].keys()
result = [lines[0][x] for x in unique0] + [lines[1][x] for x in unique1]
person max    schedule 23.08.2010
comment
Выглядит для меня хорошим ответом, я бы посоветовал только сохранять позицию поиска каждой строки при чтении, чтобы быстро восстановить их для получения результата. - person Tony Veijalainen; 23.08.2010

Имея 60 000 или 80 000 уникальных строк, вы можете просто создать словарь для каждой уникальной строки, сопоставив ее с числом. mydict["hello world"] => 1 и т. д. Если ваша средняя строка составляет около 40-80 символов, это будет около 10 МБ памяти.

Затем прочитайте каждый файл, преобразовав его в массив чисел через словарь. Они легко поместятся в памяти (2 файла по 8 байт * 3 ГБ / 60 тыс. строк меньше 1 МБ памяти). Затем diff списки. Вы можете инвертировать словарь и использовать его для печати текста строк, которые отличаются .

ИЗМЕНИТЬ:

В ответ на ваш комментарий вот пример сценария, который присваивает номера уникальным строкам при чтении из файла.

#!/usr/bin/python

class Reader:

    def __init__(self, file):
        self.count = 0
        self.dict = {}
        self.file = file

    def readline(self):
        line = self.file.readline()
        if not line:
            return None
        if self.dict.has_key(line):
            return self.dict[line]
        else:
            self.count = self.count + 1
            self.dict[line] = self.count
            return self.count

if __name__ == '__main__':
    print "Type Ctrl-D to quit."
    import sys
    r = Reader(sys.stdin)
    result = 'ignore'
    while result:
        result = r.readline()
        print result
person Harold L    schedule 23.08.2010
comment
@ Гарольд Л, я в замешательстве. Как я могу сопоставить 60 000 или 80 000 уникальных строк со словарем, не зная, какие строки содержатся в обоих файлах. - person jack; 23.08.2010
comment
Вы можете просто создать словарь по мере чтения файлов. Я добавлю код для вспомогательной функции выше. - person Harold L; 23.08.2010
comment
dict.keys() с 3 ГБ? Я не верю, что можно сохранить хэш только с помощью seff.dict[line], но он сохраняет всю строку в ключи + хэши. - person Tony Veijalainen; 23.08.2010
comment
@Tony Veijalainen, да, диктофон сохранит все строки, но каждую строку он сохранит только один раз. Таким образом, этот метод хорошо работает здесь только потому, что у Джека много повторяющихся строк: 3 ГБ могут составлять, скажем, 100 миллионов строк текста, но только 80 000 уникальных строк будут храниться в наборе ключей словаря. - person Harold L; 23.08.2010
comment
В обоих файлах нет повторяющихся строк. См. комментарий автора к его сообщению в ответ на меня. Может быть, я не понимаю его английский должным образом. - person Tony Veijalainen; 23.08.2010
comment
К сожалению, судя по разъяснению ОП (в ответ Тони), в исходных файлах нет повторяющихся строк. Идентичные строки на 99,9% относятся только к тому факту, что каждая из этих строк присутствует в обоих файлах. К сожалению, с этим уточнением ваш подход не сработает. - person max; 25.08.2010

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

uniqA = set(open('fileA', 'r'))
person Community    schedule 23.08.2010

В Python есть библиотека difflib, которая утверждает, что вполне конкурентоспособна с другими утилитами сравнения. См.: http://docs.python.org/library/difflib.html

person Tony Veijalainen    schedule 23.08.2010
comment
Может ли эта библиотека обрабатывать текстовые файлы размером 3 ГБ?! Даже хорошим базам данных трудно справиться с такой задачей... Им нужна индексация и другие оптимизации, чтобы получить результат в разумные сроки. - person Asaf; 23.08.2010
comment
Поскольку строки расположены в случайном порядке и нет необходимости искать изменения строк, возможно, это не лучший подход. Было бы уместно, если бы два файла были версиями одного и того же файла (это было возможно из-за большого сходства строк между ними). - person Tony Veijalainen; 23.08.2010