Perl — Поиск дубликатов строк в файле или массиве

#perl #line-processing

#perl #обработка строк

Вопрос:

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

Комментарии:

1. Многое зависит от размера входных данных, размеров строк и потенциального количества дубликатов. Если требования к памяти невелики, то решения с %duplicates хэшем являются адекватными.

2. Они есть. Я просто использую дескриптор файла <DATA>, чтобы быстро что-то проверить. Не похоже, что есть какие-либо дубликаты, так что это хорошо.

Ответ №1:

Использование стандартных сокращений Perl:

 my %seen;
while ( <> ) { 
    print if $seen{$_}  ;
}
  

Как «однострочный»:

 perl -ne 'print if $seen{$_}  '
  

Еще данные? Это выводит <file name>:<line number>:<line> :

 perl -ne 'print ( $ARGV eq "-" ? "" : "$ARGV:" ), "$.:$_" if $seen{$_}  '
  

Объяснение %seen :

  • %seen объявляет хэш. Для каждой уникальной строки во входных данных (которая в while(<>) данном случае поступает из) $seen{$_} будет скалярный слот в хэше, названный текстом строки (это то, что $_ делается в фигурных скобках has {} ).
  • Используя постфиксный оператор инкремента ( x ), мы берем значение для нашего выражения, не забывая увеличивать его после выражения. Итак, если мы не «видели», строка $seen{$_} не определена — но при введении в числовой «контекст», подобный этому, она принимается за 0 — и false.
  • Затем значение увеличивается до 1.

Итак, когда while начинает выполняться, все строки равны «нулю» (если это поможет, вы можете думать о строках как о «не %seen «), затем, когда мы видим строку в первый раз, perl принимает неопределенное значение — которое завершается ошибкой if — и увеличивает счетчик в скалярном слоте до 1. Таким образом, это значение равно 1 для любых будущих вхождений, в этот момент оно выполняет if условие и печатается.

Теперь, как я сказал выше, %seen объявляется хэш, но при strict выключенном режиме любое переменное выражение может быть создано на месте. Итак, когда perl видит в первый раз, $seen{$_} он знает, что я ищу %seen , у него этого нет, поэтому он создает это.

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

Комментарии:

1. Можете ли вы объяснить, как именно работает $ seen{$_} ? Я понимаю, что он присваивает значение текущей строки хэш-таблице, но что здесь делает , который заставляет его находить дубликаты?

2. $seen{$_} ссылается на значение в хэше %seen с ключом $_, который является текущей строкой. Оператор увеличит значение хэша. Это означает, что при первом появлении ключа его значение будет равно false, и печать не произойдет. В последующие разы, когда это будет видно, оно будет > 0, и поэтому печать будет выполнена, а печать без аргументов по умолчанию печатает переменную $_ .

3. Ах, значит, ключом для хэша является строка, но значением является количество раз, когда она была найдена в файле -1.

4. ботаники perl впечатляют меня до чертиков. 2, если бы я мог!

Ответ №2:

попробуйте это

 #!/usr/bin/perl -w
use strict;
use warnings;

my %duplicates;
while (<DATA>) {
    print if !defined $duplicates{$_};
    $duplicates{$_}  ;
}
  

Комментарии:

1. Я бы сделал print unless exists $duplicates{$_} . И 1 для -w , use strict и use warnings .

Ответ №3:

Печатает дубликаты только один раз:

 perl -ne "print if $seen{$_}   == 1"
  

Комментарии:

1. Это похоже sort file.txt | uniq -d (печатать только дубликаты) на типичную оболочку Unix. Существует ли простой эквивалент sort file.txt | uniq -u (печатать только уникальные строки)?

Ответ №4:

Если у вас Unix-подобная система, вы можете использовать uniq :

 uniq -d foo
  

или

 uniq -D foo
  

должно делать то, что вы хотите. Дополнительная информация: man uniq.