Найти идентификаторы в одном файле, которые не находятся в другом

9

У меня есть два файла:

abc.txt

abcd
xyz
pqrs

mno.txt

zzon
mkno
abcd
  • Я хочу проверить, есть ли «abcd» в файле mno.txt .
  • Не обязательно, что если «abcd» сначала находится в abc.txt , он также будет сначала в mno.txt .
  • В обоих файлах есть тысячи таких идентификаторов.
  • Я хочу также проверить, сколько идентификаторов не находится в mno.txt , которые находятся в abc.txt .

Как я могу это сделать?     

задан Avani badheka 26.06.2017 в 12:32
источник

4 ответа

19

Если ваша цель - найти общие или необычные строки, comm будет моей командой.

Он сравнивает два файла и показывает - в трех столбцах - строки, которые уникальны для файла 1, строки, которые уникальны для файла 2 и строки, которые появляются в обоих файлах, соответственно. Вы можете передать флаги для подавления любого из этих результатов. Например, comm -1 file1 file2 подавляет первый столбец, вещи, уникальные для файла1. comm -12 file1 file2 будет отображаться только в обоих файлах.

Есть одно большое предостережение: вход должен быть отсортирован. Мы можем обойти это.

Это покажет вам все в abc, которое не находится в mno:

comm -23 <(sort abc.txt) <(sort mno.txt)

И вы можете передать это в wc -l , чтобы получить счет.

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

Это можно продемонстрировать с помощью нескольких файлов-макетов. У меня довольно быстрый компьютер, чтобы показать разницу между подходами, мне нужен довольно набор образцов мамонта. Я отправил 10 миллионов строк из 10 символов в файл.

$ cat /dev/urandom | tr -dc '0-9' | fold -w 10 | head -10000000 > abc.txt
$ cat /dev/urandom | tr -dc '0-9' | fold -w 10 | head -10000000 > mno.txt

$ time comm -23 <(sort abc.txt) <(sort mno.txt) | wc -l
... 0m10.653s

$ time grep -Fcxv -f abc.txt mno.txt
... 0m23.920s

$ time grep -Fcwv -f abc.txt mno.txt
... 0m40.313s

$ time awk 'NR==FNR{a[$0]++};NR!=FNR && a[$0]' abc.txt  mno.txt | wc -l
... 0m12.161s

Сортировка - это то, что занимает большую часть времени в моей. Если мы притворимся, что abc.txt статичен, мы можем предварительно отсортировать его и сделать будущие сравнения намного быстрее:

$ sort abc.txt abc-sorted.txt
$ time comm -23 abc-sorted.txt <(sort mno.txt) | wc -l
... 0m7.426s

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

    
ответ дан Oli 26.06.2017 в 12:51
7

, чтобы получить список:

grep -Fwf abc.txt mno.txt

он дает вам нечто похожее на:

abcd
abcd
zef

, если вы хотите просто получить уникальный список, используйте его как:

grep -Fwf abc.txt mno.txt | sort | uniq

и получить счетчики:

grep -Fcwv -f abc.txt mno.txt
  • -F означает: интерпретировать PATTERN как список фиксированных строк вместо регулярных выражений.
  • -f получить шаблоны из FILE, которые будут abc.txt .
  • мы рассмотрим mno.txt для шаблонов
  • -c Подсчитать количество совпадений
  • -w Посмотрите только на «целые слова»: соответствующая подстрока должна либо находиться в начале строки, либо должна предшествовать несимволическому составляющему символу. Точно так же он должен быть либо в конце строки, либо сопровождаться неглавным составным символом. Символами Word являются буквы, цифры и символ подчеркивания.
  • -v Обратный поиск
ответ дан Ravexina 26.06.2017 в 12:46
3

Мы могли бы использовать awk для выполнения задания, передав два файла, сначала файл шаблона, а затем файл, который мы хотим проверить. Когда мы читаем первый файл, мы знаем, что NR==FNR и в то время мы можем читать строки в массиве. Когда NR!=FNR проверяется, установлен ли массив для такой строки.

$ cat abc.txt                                                      
abcd
xyz
pqrs
$ cat mno.txt                                                      
zzon
xyz
mkno
abcd
$ awk 'NR==FNR{a[$0]++};NR!=FNR && a[$0]' abc.txt  mno.txt         
xyz
abcd

И наоборот, мы можем отменить шаблон для печати тех строк, которые не находятся в abc.txt

$ awk 'NR==FNR{a[$0]++};NR!=FNR && ! a[$0]' abc.txt  mno.txt       
zzon
mkno

И если мы хотим напечатать счетчик, мы можем использовать sort и wc :

$ awk 'NR==FNR{a[$0]++};NR!=FNR && ! a[$0]' abc.txt  mno.txt | sort -u | wc -l         
2
    
ответ дан Sergiy Kolodyazhnyy 26.06.2017 в 16:12
2

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

Python

#!/usr/bin/env python3
import sys

with open(sys.argv[1]) as minuend_file:
    minuend = frozenset(map(str.rstrip, minuend_file))
with open(sys.argv[2]) as subtrahend_file:
    subtrahend = frozenset(map(str.rstrip, subtrahend_file))

difference = minuend - subtrahend
#print(*difference, sep='\n') # This prints the content of the set difference
print(len(difference)) # This prints the magnitude of the set difference

Использование:

python3 set-difference.py abc.txt mno.txt

Python (более эффективный)

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

#!/usr/bin/env python3
import sys

with open(sys.argv[1]) as minuend_file:
    minuend = set(map(str.rstrip, minuend_file))
with open(sys.argv[2]) as subtrahend_file:
    subtrahend = map(str.rstrip, subtrahend_file)
    minuend.difference_update(subtrahend)
    difference = minuend
    del minuend

#print(*difference, sep='\n') # This prints the content of the set difference
print(len(difference)) # This prints the magnitude of the set difference

Производительность

Учитывая abc.txt и mno.txt с 1 mio несортированными строками из 10 случайных символов ASCII-цифр (см. ответ Оли для настройки):

$ time python3 set-difference.py abc.txt mno.txt
user    0m10.453s

против.

$ export LC_COLLATE=C
$ time sort abc.txt > abc_sorted.txt
user    0m10.652s
$ time sort mno.txt > mno_sorted.txt
user    0m10.767s
$ time comm -23 abc_sorted.txt mno_sorted.txt | wc -l
9989882
user    0m1.600s

всего: 23 секунды

    
ответ дан David Foerster 27.06.2017 в 13:37