2017-01-19 6 views
2

Я пытаюсь распечатать всю строку, которая может содержать одно и то же слово дважды и болееПечатные линии с повторяющимися словами

E.g. с этим входным файлом:

cat dog cat 
dog cat deer 
apple peanut banana apple 
car bus train plane 
car train car train 

Выход должен быть

cat dog cat 
apple peanut banana apple 
car train car train. 

Я попробовал этот код и он работает, но я думаю, что должна быть более коротким путем.

awk '{ a=0;for(i=1;i<=NF;i++){for(j=i+1;j<=NF;j++){if($i==$j)a=1} } if(a==1) print $0}' 

Позже я хочу найти все такие повторяющиеся слова и удалить все повторяющиеся записи, кроме 1-го вхождения.

Так вход:

cat dog cat lion cat 
dog cat deer 
apple peanut banana apple 
car bus train plane 
car train car train 

Желаемый результат:

cat dog lion 
dog cat deer 
apple peanut banana 
car bus train plane 
car train 
+0

Я ищу регулярное выражение, что с этим не так? – Vicky

+0

regex используются в других языках, а также perl и ruby, но я хочу придерживаться регулярного выражения в awk, sed и grep, поэтому я помещаю те теги. – Vicky

+0

Удачи найти ответ в 'awk' с' regex' – Inian

ответ

3

Вы можете использовать эту GNU СЭД команды:

sed -rn '/(\b\w+\b).*\b\1\b/ p' yourfile 
  • -r активировать расширенные повторно и n деактивирует неявную печать каждую строки
  • команды p тогда только печатает линии, соответствующие предыдущему re (внутри косых черт):
    • \b\w+\b are words : an nonemtpy sequence of word charactes ( \ ш \ ) between word boundaries ( b`), это GNU расширения
    • такое слово «хранятся» в \1 для последующего повторного использования, в связи с использованием скобок
    • то мы стараемся соответствовать этому слову с \b\1\b снова что-то дополнительно (.*) между этими двумя местами.
    • и что весь фокус: матч что-то, поставить его в скобки, так что вы можете использовать его в том же ре с \1

Чтобы ответить на вторую часть вопроса, удаление удвоенные слова после того, как первый, но печать все строки (изменяющие только линии с удвоенными слов), вы можете использовать некоторые SED s магии:

sed -r ':A s/(.*)(\b\w+\b)(.*)\b\2\b(.*)/\1\2\3\4/g; t A ;' 
  • здесь мы снова используем трюк обратной связи.
  • , но мы должны учитывать вещи до, между и после наших удвоенных слов, поэтому у нас есть \2 в соответствующей части команды s, и у нас есть другие обратные ссылки в запасной части.
  • Обратите внимание, что только \2 не имеет парс в соответствующей части, и мы используем все группы в замене, поэтому мы фактически удалили второе слово пары.
  • для более повторений слова нам нужны цикл:
    • :A является этикеткой
    • t A переходит на метку, если была замена сделана в последнем s comamnd
    • это создает «во время цикла "вокруг s удалить другие повторы, слишком
+0

Спасибо Ларсу, что помогает, я думаю, в \ b \ w + \ w в третьей точке вы имеете в виду \ b \ w + \ b. Можете ли вы посоветовать вторую часть вопроса «Удаление всех повторяющихся слов, но первое вхождение? – Vicky

+0

@ user3369871 Спасибо за исправление. Пожалуйста, добавьте желаемый результат для второй части вопроса. Например, что должно произойти с линиями, которые имеют не нужно удваивать, если они будут напечатаны снова? –

+0

Только что понял, что вы опубликовали ту же логику, что и я. Никогда не знали, что обратные ссылки возможны в расширенных регулярных выражениях posix. – hek2mgl

2

Вот решение для печати только строки, содержащие повторяющиеся слова.

awk '{ 
    delete seen 
    for (i=1;i<=NF;++i) { 
    if (seen[$i]) { print ; next } 
    seen[$i] = 1 
    } 
}' 

Это решение для удаления повторяющихся слов после первого.

awk '{ 
    delete seen 
    for (i=1;i<=NF;++i) { 
    if (seen[$i]) { continue } 
    printf("%s ", $i); 
    seen[$i] = 1 
    } 
    print ""; 
}' 

Re ваш комментарий ...

Некоторые люди, сталкиваясь с проблемой, думаю, "Я знаю, я буду использовать регулярные выражения." Теперь у них есть две проблемы. - Джейми Завинский, 1997

+0

Я действительно ищу регулярное выражение, которое может это сделать. – Vicky

+1

@ EdMorton, спасибо, это хорошие предложения, я отредактировал выше. –

+1

@ EdMorton, спасибо, но я потерял интерес к этой проблеме, поскольку OP, похоже, не оценивает ответы. –

1

с egrep вы можете использовать так называемый назад ссылка:

egrep '(\b\w+\b).*\b\1\b' file 

(\b\w+\b) совпадает со словом на границах слов в захвате группы 1. \1 ссылки, совпадающие слова в шаблоне.

0

Я покажу решения в Perl, поскольку это, вероятно, самый гибкий инструмент для синтаксического анализа текста, особенно когда речь идет о регулярных выражениях.

Обнаружение Дубликаты

perl -ne 'print if m{\b(\S+)\b.*?(\b\1\b)}g' file 

где

  • -n вызывает Perl выполнить выражение передается с помощью -e для каждой входной линии;
  • \b соответствует границам слов;
  • \S+ соответствует одному или нескольким символам без пробела;
  • .*? - это не greedy совпадение для ноля или нескольких символов;
  • \1 - backreference первой группе, то есть слово \S+;
  • g глобально соответствует шаблону неоднократно в строке.

Удаление дубликатов

perl -pe '1 while (s/\b(\S+)\b.*?\K(\s\1\b)//g)' file 

где

  • -p вызывает Perl печатать линию ($_), как СЭД;
  • 1 while цикл работает до тех пор, пока замена заменяет что-то;
  • \K сохраняет часть, соответствующую предыдущему выражению;

Повторяющиеся слова (\s\1\b) заменяются пустой строкой (//g).

Почему Perl?

Регулярные выражения Perl, как известно, очень гибкие, а регулярные выражения в Perl на самом деле больше, чем просто регулярные выражения. Например, вы можете вставлять Perl-код в substitution с использованием модификатора /e. Вы можете использовать /x модификатор, который позволяет писать регулярные выражения в более удобном для чтения формате и даже использовать комментарии Perl в нем, например:

perl -pe '1 while (
    s/   # Begins substitution: s/pattern/replacement/flags 
    \b (\S+) \b # A word 
    .*?   # Ungreedy pattern for any number of characters 
    \K   # Keep everything that matched the previous patterns 
    (   # Group for the duplicate word: 
    \s   # - space 
    \1   # - backreference to the word 
    \b   # - word boundary 
) 
    //xg 
)' file 

Как вы уже заметили, \Kanchor очень удобно, но is not available in many popular tools включая awk, bash, и sed.