2009-11-03 3 views
3

префикс уникальных слов с «UNIQUE:» внутри файла, который я пытался использовать команду Perl регулярное выражение, как:Регулярное выражение для соответствия уникальных слов в файлах

perl -e 'undef $/;while($_=<>){s/^(((?!\b\3\b).)*)\b(\w+)\b(((?!\b\3\b).)*)$/\1UNIQUE:\3\4/gs;print $_;}' demo 

В файле демо, содержащий:

watermelon banana 
apple pear pineapple orange mango 
strawberry cherry 
kiwi pineapple lemon cranberry watermelon 
orange plum cherry 
kiwi banana plum 
mango cranberry apple 
lemon 

выход:

watermelon banana 
apple pear pineapple orange mango 
strawberry cherry 
kiwi pineapple lemon cranberry watermelon 
orange plum cherry 
kiwi banana plum 
mango cranberry apple 
UNIQUE:lemon 

К сожалению, \ 3 обратная ссылка не обрабатывается, если используется заранее.

Есть ли другой способ достичь этого с помощью другого регулярного выражения или с другими обычными командами, доступными на Linux box? (Grep, СЭД, AWK, ...)

Большое спасибо

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

{watermelon || banana} 
apple = (pear pineapple orange mango) 
strawberry cherry 
kiwi = pineapple = lemon = cranberry = watermelon 
orange - plum = cherry 
kiwi = banana + plum 
mango = cranberry && apple 
lemon 

Если это упрощает задачу, слова могут быть с префиксом что-то вроде $ или @.

+1

Обратите внимание, что в этом демо-файле уникальны только «груша» и «клубника». «undef $ /» используется для чтения файла в целом вместо строки за строкой. (((?! Foo).) *) Является нормальным, используемым для соответствия любому, что не содержит «foo». –

ответ

13

я вижу, вы уже используете Perl. Если вы хотите что-то рассчитывать, используя хэш всегда хороший подход ...

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

my %hash =(); 
my $str; 

while(<>) { 
    $str .= $_; 
    $_ =~ s/\W+/ /g; 
    map {$hash{$_}++} split ' ', $_; 
} 

for (keys %hash){ 
    my $word = $_; 
    if($hash{$word}==1) { 
     $str =~ s/\($word)/UNIQUE:$word/g; 
    } 
}; 

print "$str\n"; 

который будет:

 
{watermelon || banana} 
apple = (UNIQUE:pear pineapple orange mango) 
UNIQUE:strawberry cherry 
kiwi = pineapple = lemon = cranberry = watermelon 
orange - plum = cherry 
kiwi = banana + plum 
mango = cranberry && apple 
lemon 

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

Вышеприведенный фрагмент читает ввод один раз, но сохраняет весь исходный текст в $ str - очевидно, плохая идея, если вход был большим.

+0

Операция замены требует границ слов. Чтобы увидеть проблему, добавьте еще один элемент данных: «ягода». – FMc

+0

Хороший улов, я добавил границы слов в regexp –

0

Можете ли вы поместить каждое слово в одну строку? Если вы можете вы можете использовать команду Uniq:

uniq -c yourfile 

Таким образом, каждый уникальное слово будет иметь счетчик 1.

+0

К сожалению, нет, я действительно должен отмечать уникальные слова с чем-то вроде «UNIQUE:» внутри исходных файлов. –

+0

uniq сообщает или отфильтровывает повторяющиеся строки в файле, а не слова. –

5

Это невозможно сделать с помощью одного выполнения регулярного выражения. Причина этого заключается в том, что после завершения первой замены внутренний курсор перемещается в конце этого совпадения, и в следующий раз, когда он начнет соответствовать, он забывает, что стоит за ним. И как бы то ни было, динамические взгляды не поддерживаются, поэтому вы не можете проверить, появилось ли это слово до этой сопоставимой позиции. Однако вы можете заменить одно слово при каждом выполнении регулярного выражения (потому что таким образом вы всегда можете привязывать в начале строки). Итак, что вы хотите сделать, запустите следующее regexp, если оно что-то заменяет.

s/^.*?\K(?!UNIQUE:)\b(\w+)\b(?=(?:(?!\b\1\b).)*$)/UNIQUE:\1/s 
+0

+1 для не может быть сделано за одно выполнение регулярного выражения. –

+0

Хорошее объяснение! –

1

я не знаю, почему «лимон» является уникальным, но давайте просто скажем, я предполагаю, что это только один вхождение слова, то вот AWK скрипт

awk '{ 
for(i=1;i<=NF;i++){ 
    words[$i]++ 
    if(words[$i] > 1){ delete words[$i] } 
} 
a[++d]=$0 
}END{ 
for(i=1;i<=d;i++){ 
    m=split(a[i],t," ") 
    for(k=1;k<=m;k++){ 
     if (t[k] in words) { 
      t[k]="UNIQUE:"t[k] 
     } 
    } 
    for(w=1;w<=d;w++){ 
     printf "%s ",t[w] 
    } 
    print "" 
} 
}' file 

выход

$ more file 
watermelon banana 
apple pear pineapple orange mango 
strawberry cherry 
kiwi pineapple lemon cranberry watermelon 
orange plum cherry 
kiwi banana plum 
mango cranberry apple 
lemon 

$ ./shell.sh 
watermelon banana 
apple UNIQUE:pear pineapple orange mango 
UNIQUE:strawberry cherry 
kiwi pineapple lemon cranberry watermelon 
orange plum cherry 
kiwi banana plum 
mango cranberry apple 
lemon 
2

Обратите внимание, что вам нужны границы на операции замены; в противном случае уникальный apple может столкнуться с неистовым cranapple, например.

use strict; 
use warnings; 
use File::Slurp qw(read_file); 

my %words; 
my $content = read_file(shift @ARGV); 
$words{$_} ++ for split /[\s\W]+/, $content; 
my @uniq = grep { $words{$_} == 1 and length } keys %words; 
$content =~ s/\b$_\b/UNIQUE:$_/g for @uniq; 
print $content;