Получите уникальные строки в двух текстовых файлах - PullRequest
0 голосов
/ 05 июля 2018

У меня есть два несортированных текстовых файла (размером от 150 МБ до 1 ГБ).

Я хочу найти все строки, которые присутствуют в a.txt и , а не в b.txt.

a.txt содержит ->

qwe
asd
zxc
rty

b.txt содержит ->

qwe
zxc

Если я объединю a.txt и 'b.txt in c.txt`, я получу:

qwe
asd
zxc
rty
qwe
zxc

Я сортирую их по алфавиту и получаю:

asd
qwe
qwe
rty
zxc
zxc

Затем я использую режим regx для поиска (. *) \ N (\ 1) \ n и заменяю их все на null, а затем заменяю все \ n \ n несколько раз на \ n, чтобы получить "разницу" между два файла.

Теперь я не могу сделать это в Python. Я могу сделать это до сортировки, но регулярные выражения не работают в несколько строк. Вот мой код Python

f = open("output.txt", 'w')
s = open(outputfile,'r+')
for line in s.readlines():
    s = line.replace('(.*)\n(\1)\n', '')
    f.write(s)

f.close() 

1 Ответ

0 голосов
/ 05 июля 2018

Я могу сделать это до сортировки, но регулярные выражения не работают в несколько строк.

Ваше регулярное выражение в порядке. У вас нет многострочных . У вас есть одиночные строки:

for line in s.readlines():

file.readlines() читает весь файл в память в виде списка строк. Затем вы выполняете итерации по каждой из этих строк , поэтому line будет 'asd\n' или 'qwe\n', а никогда 'qwe\nqwe\n'.

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

with open('a.txt', 'r') as file_a:
    lines = set(file_a)  # all lines, as a set, with newlines

new_in_b = []
with open('b.txt', 'r') as file_b:
    for line in file_b:
        if line in lines:
            # present in both files, remove from `lines` to find extra lines in a
            lines.remove(line)
        else:
            # extra line in b
            new_in_b.append(line)

print('Lines in a missing from b')
for line in sorted(lines):
    print(line.rstrip())  # remove the newline when printing.
print()

print('Lines in b missing from a')
for line in new_in_b:
    print(line.rstrip())  # remove the newline when printing.
print()

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

with open('c.txt', 'w') as file_c:
    file_c.writelines(sorted(list(lines) + new_in_b))

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

with open('c.txt', 'r') as file_c, open('output.txt', 'w') as outfile:
    preceding = None
    skip = False
    for line in file_c:
        if preceding and preceding == line:
            # skip writing this line, but clear 'preceding' so we don't
            # check the next line against it
            preceding = None
        else:
            outfile.write(preceding)
            preceding = line
    # write out the last line
    if preceding:
        outfile.write(preceding)

Обратите внимание, что это никогда не читает весь файл в память! Итерация непосредственно над файлом дает вам отдельные строки, где файл читается кусками в буфер. Это очень эффективный метод обработки линий.

Вы также можете перебирать файл по две строки за раз, используя библиотеку itertools для отключения итератора объекта файла:

with open('c.txt', 'r') as file_c, open('output.txt', 'w') as outfile:
    iter1, iter2 = tee(file_c)  # two iterators with shared source
    line2 = next(iter2, None)  # move second iterator ahead a line
    # iterate over this and the next line, and add a counter
    for i, (line1, line2) in enumerate(zip(iter1, iter2)):
        if line1 != line2:
            outfile.write(line1)
        else:
            # clear the last line so we don't try to write it out
            # at the end
            line2 = None
    # write out the last line if it didn't match the preceding
    if line2:
        outfile.write(line2)

Третий подход - использовать itertools.groupby() для группировки строк, которые равны между собой. Затем вы можете решить, что делать с этими группами:

from itertools import groupby

with open('c.txt', 'r') as file_c, open('output.txt', 'w') as outfile:
    for line, group in groupby(file_c):
        # group is an iterator of all the lines in c that are equal
        # the same value is already in line, so all we need to do is
        # *count* how many such lines there are:
        count = sum(1 for line in group)  # get an efficient count
        if count == 1:
            # line is unique, write it out
            outfile.write(line)

Я предполагаю, что не имеет значения, если есть 2 или более копий одной и той же строки. Другими словами, вы не хотите сопряжение , вы хотите найти только уникальные линии (те, которые присутствуют только в a или b).

Если ваши файлы очень большие , но уже отсортированы , вы можете использовать метод сортировки слиянием, без необходимости объединять два файла в один вручную. Функция heapq.merge() дает вам строки из нескольких файлов в отсортированном порядке, если входные данные отсортированы по отдельности. Используйте это вместе с groupby():

import heapq
from itertools import groupby

# files a.txt and b.txt are assumed to be sorted already
with open('a.txt', 'r') as file_a, open('b.txt', 'r') as file_b,\
        open('output.txt', 'w') as outfile:
    for line, group in groupby(heapq.merge(file_a, file_b)):
        count = sum(1 for line in group)
        if count == 1:
            outfile.write(line)

Опять же, эти подходы только читают достаточно данных из каждого файла, чтобы заполнить буфер. Итератор heapq.merge() одновременно хранит в памяти только две строки, как и groupby(). Это позволяет обрабатывать файлы любого размера независимо от ограничений памяти.

...