Как посчитать строки, содержащие одно из двух слов, но не оба

Я понял это, когда собирался опубликовать вопрос.

Команда «чтение» в моем первом цикле покажет строку только после того, как в консоли появится символ новой строки. Это означает, что мой сценарий не мог ответить на приглашение, пока не появилась новая строка; это означало, что окно было закрыто.

Чтобы исправить это, я изменил проверку, чтобы искать строку, которая появляется над фактической подсказкой, за которой следует сон на 1/100 секунды перед выдачей моей записи.

Это решение позволяет автоматически останавливать процессы загрузки.

5
05.02.2021, 14:35
8 ответов

С grep:

cat poem.txt \
  | grep -Evi -e '\<an\>.*\<the\>' -e '\<the\>.*\<an\>' \
  | grep -Eci -e '\<(an|the)\>'

При этом подсчитываются совпавших строк . Вы можете найти альтернативный синтаксис, который подсчитывает общее количество совпадений ниже.

Разбивка:

Команда frist grep отфильтровывает все строки, содержащие как 'an', так и 'the'. Вторая команда grep подсчитывает те строки, которые содержат либо «an», либо «the».

Если вы удалите cиз второго grep -Eci, вы увидите, что все совпадения выделены.

Детали:

  • Опция -Eвключает расширенный синтаксис выражений (ERE )для grep.

  • Параметр -iсообщает grep, что он должен соответствовать регистру -без учета регистра

  • Опция -vуказывает grep инвертировать результат (, т. е. строки соответствия , не содержащие шаблон)

  • Опция -cуказывает grep выводить количество совпавших строк вместо самих строк

  • Узоры:

    1. \<соответствует началу слова (спасибо @glenn -jackman)
    2. \>соответствует концу слова (спасибо @glenn -jackman)

    --> Таким образом, мы можем быть уверены, что слова , содержащие 'the' или 'an' (, такие как 'pan' )

    , не совпадают.
      Таким образом,
    1. grep -Evi -e '\<an\>.*\<the\>'соответствует всем строкам , кроме , содержащим 'an... the'

    2. Аналогично, grep -Evi -e '\<the\>.*\<an\>'соответствует всем строкам , не , содержащим 'the... an'

    3. grep -Evi -e '\<an\>.*\<the\>' -e '\<the.*an\>'представляет собой комбинацию 3. и 4.

    4. grep -Eci -e '\<(an|the)\>'соответствует всем строкам, содержащим «an» или «the» (, окруженным пробелом или началом/концом строки ), и печатает количество совпадающих строк

РЕДАКТИРОВАТЬ 1:Используйте \<и \>вместо ( |^)и ( |$), как предложил @glenn -jackman

РЕДАКТИРОВАТЬ 2:Чтобы подсчитать количество совпадений вместо количества совпавших строк, используйте следующее выражение:

cat poem.txt \
  | grep -Evi -e '\<an\>.*\<the\>' -e '\<the\>.*\<an\>' \
  | grep -Eio -e '\<(an|the)\>' \
  | wc -l

Здесь используется опция -oкоманды grep, которая печатает каждое совпадение в отдельной строке (и больше ничего ), а затем wc -lдля подсчета строк.

8
18.03.2021, 22:32

Следующая программа GNU awkдолжна помочь:

awk '(/(^|\W)[Tt]he(\W|$)/ && !/(^|\W)[Aa]n(\W|$)/) || (/(^|\W)[Aa]n(\W|$)/ && !/(^|\W)[Tt]he(\W|$)/) {c++} END{print c}' poem.txt

Это увеличит значение счетчика c, если

  • строка соответствует(^|\W)[Tt]he(\W|$)(первой -букве -регистру -нечувствительна the, предшествует не -составляющая слова(\W)или начало строки (^), а затем не -составляющая слова(\W)или конец -строки ($)), но не(^|\W)[Aa]n(\W|$)(изолированная первая -буква -регистр -нечувствительныйan)-ИЛИ-
  • строка соответствует (^|\W)[Aa]n(\W|$), но не(^|\W)[Tt]he(\W|$)

В конце выведите значение c.

Его можно сформулировать немного короче, используя \<и \>для «начала -слова -» и «конца -слова -»:

awk '(/\<[Tt]he\>/ && !/\<[Aa]n\>/) || (/\<[Aa]n\>/ && !/\<[Tt]he\>/) {c++} END{print c}' poem.txt

Еще короче было бы:

awk '/\<[Tt]he\>/ != /\<[Aa]n\>/ {c++} END{print c}' poem.txt

, так как неравенство всегда истинно только в том случае, если одно из них, но не оба (и ни одно из )из anи theприсутствуют на линии.

Для этого подхода требуется GNU awk, поскольку конструкции \Wи \</\>являются расширениями GNU для расширенного синтаксиса регулярных выражений (, но \</\>также понимаются . ] Регулярные выражения BSD).

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

5
18.03.2021, 22:32
perl -nE 'END {say $c+0} ++$c if /\bthe\b/i xor /\ban\b/i' file
gawk 'END {print c+0} /\<the\>/ != /\<an\>/ {++c}' IGNORECASE=1 file

Сравнение результатов сопоставления каждого выражения может дать желаемый результат.

Например, результатом сопоставления \<the\>может быть либо 0, либо 1. Если результат другого совпадения такой же, то оба регулярных выражения либо найдены, либо не найдены, и строка не должна учитываться. Если они различаются, это означает, что одно совпадение было найдено, а другое нет, поэтому счетчик увеличивается.

gawk имеет встроенную -в xor()функцию:

gawk 'END {print c+0} xor(/\<the\>/,/\<an\>/) {++c}' IGNORECASE=1 file
10
18.03.2021, 22:32

Вы можете сделать это с помощью GNU grep и PCRE с нулевыми -утверждениями длины:

grep -iP '(?=.*\bthe\b)(?!.*\ban\b)|(?=.*\ban\b)(?!.*\bthe\b)' poem.txt

Where is the misty shark?
...
Eat an orange.
...

grep -ciP '(?=.*\bthe\b)(?!.*\ban\b)|(?=.*\ban\b)(?!.*\bthe\b)' poem.txt

9

Та же функция доступна в perl (, где она возникла ), и perl может присутствовать на машинах, где нет GNU grep.

3
18.03.2021, 22:32

Эта команда GNU awk сделает то, что вам нужно:

$ awk '
        function lnxor(a,b) {return !(a&&b||(!a&&!b))};
        { str=tolower($0); a=(str~/\<the\>/); b=(str~/\<an\>/); 
          c=lnxor(a,b); count+=c 
        } END {print count}
' poem.txt

9

Инксор — это л логическое n от исключающее ИЛИ из aи b.

Где aистинно, если строка содержит theи b истинно, если строка содержит an.

Таким образом, довольно легко распространить идею на большее количество слов.

В GNU awk также можно использовать IGNORECASE(, установив его в не -ноль )вместо tolower(), чтобы игнорировать случай совпадения строки с регулярным выражением. Однако это изменит все операции со строками и регулярными выражениями, включая эффекты FS и RS. Поскольку изменение (, которое сработает в данном случае, ), его следует использовать с большей осторожностью. Это не относится к какому-то конкретному тесту.

2
18.03.2021, 22:32

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

perl -lne '$k += (0,1,0)[/\ban\b/i+/\bthe\b/i]}{print 0+$k' poems.txt
9
2
18.03.2021, 22:32

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

gawk 'xor(/\<an\>/,/\<the\>/)' IGNORECASE=1./poem.txt | wc -l
2
18.03.2021, 22:32

Это позволит вам выводить строки, содержащие любое количество любого набора слов, и будет работать с использованием любого awk в любой оболочке на каждом компьютере Unix:

$ cat tst.awk
BEGIN {
    FS = "[^[:alnum:]_]+"
    split(tolower(tgts),tmp)
    for (i in tmp) {
        targets[tmp[i]]
    }
    tgtsRequired = (tgtsRequired ? tgtsRequired : 1)
}
{
    delete present
    for (i=1; i<=NF; i++) {
        present[tolower($i)]
    }

    tgtsPresent = 0
    for (target in targets) {
        tgtsPresent += (target in present ? 1 : 0)
    }
}
tgtsPresent == tgtsRequired

$ awk -v tgts='the an' -f tst.awk poem.txt
Where is the misty shark?
The small reef roughly fights the mast.
Where is the small gull?
The gull grows like a clear pirate.
Eat an orange.
Elvis Aaron Presley also known simply as the Elvis
He is also referred to as the King
The best-selling solo music artist of all time
He was the most commercially successful artist in many genres

$ awk -v tgts='the an' -v tgtsRequired=2 -f tst.awk poem.txt
Elvis in the 1970s has numerous jumpsuits including an eagle one.

$ awk -v tgts='the an eagle' -v tgtsRequired=2 -f tst.awk poem.txt

$ awk -v tgts='the an eagle' -v tgtsRequired=3 -f tst.awk poem.txt
Elvis in the 1970s has numerous jumpsuits including an eagle one.

Под "словом" подразумевается любая непрерывная строка буквенных -цифровых -или -символов подчеркивания.

3
18.03.2021, 22:32

Теги

Похожие вопросы