Как удалить дубликаты фраз из документа? - PullRequest
2 голосов
/ 09 января 2012

Есть ли простой способ удалить дублирующееся содержимое из большого текстового файла?Было бы замечательно иметь возможность обнаруживать повторяющиеся предложения (разделенные знаком «.» Или даже лучше находить дубликаты фрагментов предложений (например, фрагменты текста из 4 слов).

Ответы [ 6 ]

2 голосов
/ 09 января 2012

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

Лично я рекомендую Python , и это NLTK (инструментарий естественного языка). Прежде чем углубиться в это, возможно, стоит немного прочесть о НЛП, чтобы вы знали, что на самом деле нужно делать. Например, «кусочки текста из 4 слов» известны в литературе как 4 грамма ( n-грамм в общем случае). Инструментарий поможет вам найти их и многое другое.

Конечно, есть, вероятно, альтернативы Python / NLTK, но я с ними не знаком.

1 голос
/ 09 января 2012

Удалите повторяющиеся фразы, сохраняя оригинальный порядок:

nl -w 8 "$infile" | sort -k2 -u | sort -n | cut -f2

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

0 голосов
/ 09 января 2012

Я только что создал скрипт на python, который делает в значительной степени то, что я хотел изначально:

import string
import sys

def find_all(a_str, sub):
    start = 0
    while True:
        start = a_str.find(sub, start)
        if start == -1: return
        yield start
        start += len(sub)

if len(sys.argv) != 2:
    sys.exit("Usage: find_duplicate_fragments.py some_textfile.txt")
file=sys.argv[1]
infile=open(file,"r")
text=infile.read()
text=text.replace('\n','') # remove newlines
table = string.maketrans("","")
text=text.translate(table, string.punctuation) # remove punctuation characters
text=text.translate(table, string.digits) # remove numbers
text=text.upper() # to uppercase
while text.find("  ")>-1:
    text=text.replace("  "," ") # strip double-spaces

spaces=list(find_all(text," ")) # find all spaces

# scan through the whole text in packets of four words
# and check for multiple appearances.
for i in range(0,len(spaces)-4): 
    searchfor=text[spaces[i]+1:spaces[i+4]]
    duplist=list(find_all(text[spaces[i+4]:len(text)],searchfor))
    if len(duplist)>0:
        print len(duplist),': ',searchfor

Кстати: я новичок в Python, поэтому любые советы по улучшению практики Python приветствуются!

0 голосов
/ 09 января 2012

Краткий ответ: нет простого метода.В общем, любое решение должно сначала решить, как разбить входной документ на куски (предложения, наборы из 4 слов каждое и т. Д.), А затем сравнить их, чтобы найти дубликаты.Если важно, чтобы упорядочение элементов, не являющихся дубликатами, было одинаковым в выходных данных, как это было во входных данных, то это только усложняет ситуацию.

Самое простое дружественное к bash решение - разделить входные данные настроки, основанные на любых критериях, которые вы выбираете (например, разделить на каждом ., хотя выполнение этой цитаты безопасно немного сложно), а затем использовать стандартные механизмы обнаружения дубликатов (например, | uniq -c | sort -n | sed -E -ne '/^[[:space:]]+1/!{s/^[[:space:]]+[0-9]+ //;p;}', а затем, для каждой результирующей строки, удалить текстиз ввода.

Если предположить, что у вас есть файл, который был правильно разделен на строки в «предложении», тогда

uniq -c lines_of_input_file | sort -n | sed -E -ne '/^[[:space:]]+1/!{s/^[[:space:]]+[0-9]+ //;p;}' | while IFS= read -r match ; do sed -i '' -e 's/'"$match"'//g' input_file ; done

Может быть достаточно. Конечно, он ужасно сломается, если $match содержит любые данные, которые sed интерпретирует как шаблон. Для выполнения фактической замены следует использовать другой механизм, если это представляет для вас проблему.

Примечание. Если вы используете GNU sed, -EПереключатель выше должен быть изменен на -r

0 голосов
/ 09 января 2012

Вы можете использовать grep с обратными ссылками. Если вы напишите grep "\([[:alpha:]]*\)[[:space:]]*\1" -o <filename>, это будет совпадать с любыми двумя одинаковыми словами, следующими друг за другом. То есть если содержимое файла this is the the test file, будет выведено the the.

(Пояснение [[:alpha:]] соответствует любому символу az и AZ, звездочка * после того, как это означает, что может появляться столько раз, сколько нужно, \(\) используется для группировки, чтобы отозвать ее позже, затем [[:space:]]* соответствует любому количеству пробелов и табуляций, и, наконец, \1 соответствует точной найденной последовательности, заключенной в \(\) скобки)

Аналогично, если вы хотите сопоставить группу из 4 слов, которая повторяется два раза подряд, выражение будет выглядеть как grep "\(\([[:alpha:]]*[[:space]]*\)\{4\}[[:space:]]*\1" -o <filename> - оно будет соответствовать, например, a b c d a b c d.

Теперь нам нужно добавить произвольную последовательность символов между совпадениями. Теоретически это должно быть сделано с помощью вставки .* непосредственно перед обратными ссылками, то есть grep "\(\([[:alpha:]]*[[:space]]*\)\{4\}.*\1" -o <filename>, но, похоже, это не работает для меня - оно соответствует только любой строке и игнорирует указанную обратную ссылку

0 голосов
/ 09 января 2012

Вы можете удалить дубликаты строк (которые должны быть ровно равными) с помощью uniq, если вы sort ваш текстовый файл первым.

$ cat foo.txt
foo
bar
quux
foo
baz
bar
$ sort foo.txt
bar
bar
baz
foo
foo
quux
$ sort foo.txt | uniq
bar
baz
foo
quux

Кроме того, простой способ сделать то, что вы хотите, не существует.(Как вы будете даже разделять предложения?)

...