如何计算包含两个单词之一但不包含两个单词的行数

如何计算包含两个单词之一但不包含两个单词的行数

我需要计算文本文件 ( ) 中包含单词the和 的行数,但是anpoem.txt不是同时包含两者的

我尝试过使用

grep -c the poem.txt | grep -c an poem.txt

the但是当和 的总数an为 9 行时,这给了我 6 的错误答案。

我确实想计算包含单词的行数,而不是单词本身。只有实际单词才算数,so thebut notthere和 and anbut not Pan

示例文件:poem.txt

Where is the misty shark?
Where is she?
The small reef roughly fights the mast.
Where is the small gull?
Where is he?
The gull grows like a clear pirate.
Clouds fall like old mainlands.

She will Rise calmly like a dead pirate.
Eat an orange.
Warm, sunny sharks quietly pull a cold, old breeze.
All ships command rough, rainy sails.

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

He has many awards including a Grammy lifetime achievement
Elvis in the 1970s has numerous jumpsuits including an eagle one.

进一步澄清:这首诗中有多少行包含 the或但你不应该计算同时包含和an的行。thean

the car is red - this counted
an apple is in the corner - not counted
hello i am big - not counted
where is an apple - counted

所以这里的输出应该是2。

编辑:我不担心区分大小写。

突出显示的单词的屏幕截图

最终编辑:感谢您的所有帮助。我已经成功解决了这个问题。我使用了答案之一并对其进行了一些更改。我 曾经如何将第二个 grep 中cat poem.txt | grep -Evi -e '\<an .* the\>' -e '\<the .* an\>' | grep -Eci -e '\<(an|the)\>的 更改为 a以获取一些附加信息。再次感谢您的所有帮助! :)-c-n

答案1

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

答案2

使用 grep:

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

这算的是匹配线。您可以找到一种替代语法来计算总数火柴在下面。

分解:

首先 grep 命令过滤掉所有包含“an”和“the”的行。第二个 grep 命令对包含“an”或“the”的行进行计数。

c如果您从第二个 grep 中删除-Eci,您将看到所有匹配项都突出显示。

细节:

  • -E选项为 grep 启用扩展表达式语法 (ERE)。

  • -i选项告诉 grep 匹配时不区分大小写

  • -v选项告诉 grep 反转结果(即匹配行不是包含模式)

  • -c选项告诉 grep 输出匹配行的数量而不是行本身

  • 模式:

    1. \<匹配单词的开头(谢谢@格伦-杰克曼
    2. \>匹配单词的结尾(谢谢@格伦-杰克曼

    --> 这样我们就可以确保不匹配单词含有“the”或“an”(如“pan”)

    1. grep -Evi -e '\<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的建议,使用\<and\>代替( |^)and( |$)

编辑2:为了计算匹配数而不是匹配行数,请使用以下表达式:

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

这使用了-ogrep 选项,它将每个匹配打印在单独的行中(没有其他内容),然后wc -l对行进行计数。

答案3

下面的 GNUawk程序应该可以解决这个问题:

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

可以使用\<and\>作为“词开头”和“词结尾”来将其表述得稍微短一些:

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因为\Wand \</\>结构是扩展正则表达式语法的 GNU 扩展(但\</\>也可以被理解为BSD 正则表达式)。

请注意,您在自己尝试的解决方案中显示的管道构造将不起作用,因为使用grep文件作为输入参数进行调用取代了从 stdin 读取,因此管道的第一部分会在不被注意的情况下消失,输出完全是由于最后一部分(查找 的出现an,甚至是嵌入在其他单词中的那些)。

答案4

您可以使用 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 不存在的机器上。

相关内容