Удаление дублированных строк из текстового файла - PullRequest
5 голосов
/ 09 февраля 2011

Я обрабатываю большие текстовые файлы (~ 20 МБ), содержащие данные, разделенные линией. Большинство записей данных дублируются, и я хочу удалить эти дубликаты, чтобы сохранить только одну копию.

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

например. Мне нужно идти от этого:

BOB 123 1DB
JIM 456 3DB AX
DAVE 789 1DB
BOB 123 1DB
JIM 456 3DB AX
DAVE 789 1DB
BOB 123 1DB EXTRA BITS
к этому:
JIM 456 3DB AX
DAVE 789 1DB
BOB 123 1DB EXTRA BITS
NB. окончательный заказ не имеет значения.

Какой эффективный способ сделать это?

Я могу использовать awk, python или любой стандартный инструмент командной строки linux.

Спасибо.

Ответы [ 8 ]

12 голосов
/ 09 февраля 2011

Как насчет следующего (в Python):

prev = None
for line in sorted(open('file')):
  line = line.strip()
  if prev is not None and not line.startswith(prev):
    print prev
  prev = line
if prev is not None:
  print prev

Если вы обнаружите, что использование памяти является проблемой, вы можете выполнить сортировку как этап предварительной обработки, используя Unix sort (который основан на диске ), и изменить скрипт так, чтобы он не прочитать весь файл в память.

3 голосов
/ 09 февраля 2011

awk '{x[$1 " " $2 " " $3] = $0} END {for (y in x) print x[y]}'

Если вам нужно указать количество столбцов для разных файлов:

awk -v ncols=3 '
  {
    key = "";
    for (i=1; i<=ncols; i++) {key = key FS $i}
    if (length($0) > length(x[key])) {x[key] = $0}
  }
  END {for (y in x) print y "\t" x[y]}
'
2 голосов
/ 09 февраля 2011

Этот вариант ответа Гленна Джекмана должен работать независимо от положения строк с дополнительными битами:

awk '{idx = $1 " " $2 " " $3; if (length($0) > length(x[idx])) x[idx] = $0} END {for (idx in x) print x[idx]}' inputfile

или

awk -v ncols=3 '
  {
    key = "";
    for (i=1; i<=ncols; i++) {key = key FS $i}
    if (length($0) > length(x[key])) x[key] = $0
  }
  END {for (y in x) print x[y]}
' inputfile
2 голосов
/ 09 февраля 2011

Этот или небольшой вариант должен делать:

finalData = {}
for line in input:
    parts = line.split()
    key,extra = tuple(parts[0:3]),parts[3:]
    if key not in finalData or extra:
        finalData[key] = extra

pprint(finalData)

вывод:

{('BOB', '123', '1DB'): ['EXTRA', 'BITS'],
 ('DAVE', '789', '1DB'): [],
 ('JIM', '456', '3DB'): ['AX']}
1 голос
/ 09 февраля 2011

Функция find_unique_lines будет работать для файлового объекта или списка строк.

import itertools

def split_line(s):
    parts = s.strip().split(' ')
    return " ".join(parts[:3]), parts[3:], s

def find_unique_lines(f):
    result = {}
    for key, data, line in itertools.imap(split_line, f):
        if data or key not in result:
            result[key] = line
    return result.itervalues()

test = """BOB 123 1DB
JIM 456 3DB AX
DAVE 789 1DB
BOB 123 1DB
JIM 456 3DB AX
DAVE 789 1DB
BOB 123 1DB EXTRA BITS""".split('\n')

for line in find_unique_lines(test):
        print line
BOB 123 1DB EXTRA BITS
JIM 456 3DB AX
DAVE 789 1DB
1 голос
/ 09 февраля 2011

Если у вас есть Perl и вы хотите сохранить только последнюю запись:

cat file.txt | perl -ne 'BEGIN{%k={}} @_ = split(/ /);$kw = shift(@_); $kws{$kw} = "@_"; END{ foreach(sort keys %kws){ print "$_ $kws{$_}";} }' > file.new.txt
1 голос
/ 09 февраля 2011

Поскольку вам нужны дополнительные биты, самый быстрый способ - создать набор уникальных записей (сортировка -u сделает), а затем вы должны сравнить каждую запись друг с другом, например,

<code>if x.startswith(y) and not y.startswith(x)
и просто оставь x и отбрось y.
1 голос
/ 09 февраля 2011

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

def split_extra(s):
    """Return a pair, the important bits and the extra bits."""
    return blah blah blah

data = {}
for line in open('file'):
    impt, extra = split_extra(line)
    existing = data.setdefault(impt, extra)
    if len(extra) > len(existing):
        data[impt] = extra

out = open('newfile', 'w')
for impt, extra in data.iteritems():
    out.write(impt + extra)
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...