【问题标题】:AWK program using regex to count matching lines使用正则表达式计算匹配行数的 AWK 程序
【发布时间】:2016-06-16 02:24:05
【问题描述】:

该程序应该计算以括号中的十进制数字开头的行数,包含大小写字母的混合,并以句点结尾。

我有

BEGIN {x=0}
/^\([0-9[0-9]*) [A-Z][A-z]* [a-z][a-z]* \.$/ {x = x+1}
END{print x}

我将它们拆分为多个不同的行,因为我一直在运行 display(!d) 语句以进行调试以试图弄清楚。 运行我使用awk -f programName.awk filename.txt 任何帮助表示赞赏。

更新

新代码读取

BEGIN{x=0}
/^\([0-9]+\)[A-Za-z]+\.$/{x++}
END{print x}

我使用 vim EC.awk 来编辑它。 awk -f EC.awk EC.txt 运行 回来了 1。 EC.txt 包含应计算的 12 行中的 5 行。

输入文件 vim EC.txt

(1) Line one, this should count.
(2)Line two. Should also count.
3 should not count..
4 not
(5)Yes.
(6). nope
7 OHHH mann
8 This suck
(9)Oh ya? YOU SUCK.
10 Cheaa
(11) BOI.
(12) WoW MoM. Print mofo.

更新代码

BEGIN{x=0}
/^\([0-9]+\).*?[A-Za-z]+\.$/{x++}
END{print x}

这给了我 6。我相信它的计数线 11 (11) BOI。正在打印这些行以确保。

【问题讨论】:

  • 不确定您的正则表达式是否正确。 ^\([0-9]\)[A-Za-z]*\.$ 是一行以括号中的单个数字开头,后跟 u/l 大小写字母并以句点结尾的行 ?多位数字改为^\([0-9]+\)[A-Za-z]*\.$
  • 我改变了你的建议,但仍然得到我想要的相同结果,如果我把 {x=x+1} 放在下一行它会打印/计数 12 这是行数我的测试文件,但应该只计算 5 行。如果我在行尾有 {x=x+1} 它打印/计数 1。
  • 您可以发布您的EC.txt 文件吗?因为那时我可以根据输入文件更改正则表达式
  • 给定该输入的预期输出是什么?您希望匹配哪些行?

标签: regex bash awk


【解决方案1】:

有关更简单、更清楚地表达意图并且还可以识别区域设置(并不总是只匹配 ASCII 字母)的替代解决方案,请参阅Ed Morton's helpful answer

尝试以下方法(符合 POSIX):

awk '/^\([0-9]+\).*([A-Z].*[a-z]|[a-z].*[A-Z]).*\.$/ { ++x } END { print x+0 }' file
  • ^\([0-9]+\) 匹配一行开头括号中的十进制数。

  • \.$ 匹配一行end处的文字句点。

  • .*([A-Z].*[a-z]|[a-z].*[A-Z]).* 匹配其间的任何字符串:

    • 任一:包含至少 1 个大写字母,后跟至少 1 个小写字母。
    • :包含至少 1 个小写字母后跟至少 1 个大写字母。
    • 因此,只要存在至少 1 个大写字母和 1 个小写字母,此表达式就应该匹配包含任意大小写 [ASCII-only] 字母组合的任何字符串。

至于为什么你的方法不起作用

  • 您的初始解决方案尝试 [A-Z][A-z] *[a-z][a-z]* 仅匹配行上第一个 [ASCII] 字母为 大写 的行;换句话说:第一个字母为小写的行不匹配。
  • 您后来的解决方案尝试,[A-Za-z]+,由于使用单个字符集 any 的字符匹配, 匹配包含 only 的行em> 大写 小写字母,这就是为什么行 (11) BOI. 也匹配。

【讨论】:

    【解决方案2】:

    idk 这是否是预期的输出,因为您没有在您的问题中包含它,但我只是编码了您在问题count the number of lines begin with a decimal number in parenthesis, containing a mix of both upper and lower case letters and end with a period 中所说的内容并添加了print,因此您可以看到它匹配的内容,所以采取看看它是否符合您的要求:

    $ cat tst.awk
    /^\([0-9]+\)/ && /[[:upper:]]/ && /[[:lower:]]/ && /\.$/ { print; cnt++ }
    END { print cnt+0 }
    
    $ awk -f tst.awk file
    (1) Line one, this should count.
    (2)Line two. Should also count.
    (5)Yes.
    (9)Oh ya? YOU SUCK.
    (12) WoW MoM. Print mofo.
    5
    

    不要误以为 awk 语句的条件部分必须是正则表达式,就像这是 sed 或 grep,因为它不是 - 它可以是正则表达式段的 ands/or 的复合条件如果这就是让您的代码更简单和更清晰的原因,就像在这种情况下恕我直言。

    【讨论】:

    • ++ 用于区域意识和意图的清晰性;我没有测试过,但也许你从经验中知道:就性能而言,这与我的单正则表达式解决方案相比如何(假设两种解决方案都使用[A-Z] / [a-z][[:upper:]] / [[:lower:]]) ?
    • 对不起,不,我不知道。我想我记得有人在另一个问题中进行了测试,显示字符类比等效范围稍快,但是一旦你添加了 ands/or 和其他所有内容,我就猜不出哪个解决方案会更快。无论如何,即使是大文件,它们也会在眨眼间运行。
    【解决方案3】:

    有时最好将条件分解为单独的正则表达式:

    1. 行以括号中的十进制数字开头:/^\([0-9]+\)//^\([[:digit:]]+\)/
    2. 包含大写字母:/[A-Z]//[[:upper:]]/
    3. 包含小写字母:/[a-z]//[[:lower:]]/
    4. 以句点结尾:/\.[ \t]*$/[ \t]* 会捕获尾随空格,如果有的话...)

    现在只需结合这些条件:

    awk '/^\([[:digit:]]+\)/ && /\.[ \t]*$/ && /[[:lower:]]/ && /[[:upper:]]/ { print }' file
    (1) Line one, this should count.
    (2)Line two. Should also count.
    (5)Yes.
    (9)Oh ya? YOU SUCK.
    (12) WoW MoM. Print mofo.
    

    然后运行wc -l获取行数:

    awk '//^\([[:digit:]]+\)/ && /\.[ \t]*$/ && /[[:lower:]]/ && /[[:upper:]]/ { print }' file | wc -l
    5
    

    或者,保持自己的计数:

    awk '/^\([[:digit:]]+\)/ && /\.[ \t]*$/ && /[[:lower:]]/ && /[[:upper:]]/ { i++ } END{print i}' file
    5
    

    你的正则表达式的问题:

    /^\([0-9]+\).*?[A-Za-z]+\.$/
                ^^                       Any string of characters
                     ^ ^                 Could be 'UPPER' or 'lower' 
    
    1. .* 匹配前面的所有字符(包括空格),
    2. [A-Za-z]+ 匹配一系列大写和/或小写字母,但不会告诉您是否同时拥有。

    几乎,但是您没有正确检测到无法在该正则表达式中同时包含大写和小写字母的行。

    【讨论】:

    • 您应该提到 \s 是 gawk 特定的,并且在某些语言环境中 a-z 表示 aAbBcC...zA-Z 表示 AbBcC...zZ 所以 /[a-z]/ && /[A-Z]/ 将满足于字母c 例如。同样在 END 中,您需要说 print i+0,因此如果没有匹配项,它将打印零而不是空行。
    • 别想了,让我谷歌一下,我会回复你的。
    • 找到一个 - 根据teaching.idallen.com/cst8177/13w/notes/… en_US.utf8 就是这样一种语言环境。我从来没有发现任何建议 [0-9] 包含除数字以外的任何内容,所以为了简洁起见,我仍然倾向于使用 [[:digit:]]
    • 至于[[:digit:]] vs [0-9] 考虑this list :-0
    • @EdMorton:您的第一个链接告诉我,2008 年的 POSIX 将“C”和“POSIX”以外的区域设置中的范围行为声明为 undefined。在实践中,似乎至少最常见的实用程序表现出 传统 行为,范围为 [a-z][A-Z](仅限 ASCII,如预期的那样区分大小写),甚至在诸如en_US.UTF-8 之类的语言环境中。以下命令证明(没有匹配项):for char in 'c' 'ü'; do grep '[A-Z]' <<<"$char"; sed -n '/[A-Z]/p' <<<"$char"; awk '/[A-Z]/' <<<"$char"; tr -dC 'A-Z' <<<"$char"; done
    【解决方案4】:

    您的正则表达式尝试匹配以下文本 (1 or more digits)<space><1 or more Uppercase><space><1 or more lowercase><space><period>

    我认为在发布问题时您错过了] 在数字的情况下,如果您想要小写后跟大写,那么您必须使用您的正则表达式;但是由于您在问题中提到它可以是大写和小写的混合,您将不得不使用[A-Za-z]++ 确保 1 个或多个,即 [a-z]+ 等同于 [a-z][a-z]*

    $cat file.txt 
    (1) aBCdadg .
    (2) dgshdf .
    (3) DFHFH .
    xyz
    abcd
    (56) sdflgkfd .
    $ cat prgm.awk 
    BEGIN {x=0}
    
    /^\([0-9]+\) [A-Za-z]+ \.$/ {x++}
    
    END {print x}
    $ awk -f prgm.awk file.txt 
    4
    $
    

    如果你想要 1 个或多个小写字符后跟 1 个或多个大写字符,那么你将不得不使用这个正则表达式:

    /^\([0-9]+\) [a-z]+ [A-Z]+ \.$/ {x++}
    

    编辑:

    $ cat file.txt 
    (1) Line one, this should count.
    (2) Line two. Should also count.
    3 should not count..
    4 not
    (5)Yes.
    (6). nope
    7 OHHH mann
    8 This suck
    (9) Oh ya? YOU SUCK.
    10 Cheaa
    (11) BOI.
    (12) WoW MoM. Print mofo.
    $ cat prgm.awk 
    BEGIN {x=0}
    
    /^\([0-9]+\)\s*[A-Za-z0-9., ]+\s*\./{x++}
    
    END {print x}
    $ awk -f prgm.awk file.txt 
    5
    $
    

    编辑 2:对不起,我急着去某个地方,并且有几个小时没有参加比赛。由于它更清楚你需要什么,我将更新完整的答案。

    $ cat prgm.awk 
    BEGIN {x=0}
    
    /^\([0-9]+\).*([A-Z].*[a-z]|[a-z].*[A-Z]).*\.$/{x++;print $0}
    
    END {print x}
    $ awk -f prgm.awk input_file.txt 
    (1) Line one, this should count.
    (2) Line two. Should also count.
    (5)Yes.
    (9) Oh ya? YOU SUCK.
    (12) WoW MoM. Print mofo.
    5
    $
    

    请通过接受除我之外的任何人的回答来标记已解决的问题:P :)

    编辑 3:给予他人信任。

    【讨论】:

    • 感谢您的帮助,我将程序更改为使用基于 jas 注释的 + 以及其他堆栈溢出问题。您的回答断言这是正确的方法。感谢您澄清 + 的作用。我将 {x=x+1} 更改为 {x++} 我意识到这是同一件事,现在我的代码与您的匹配。我运行它,现在它打印/计数为 0。理论上,一切似乎/看起来都正确。令人沮丧。
    • @ChrisFocker 修改了正则表达式
    • 好的,这就是打印第 11 行 BOI。不应该打印。条件是一个括号,里面有一个数字,后跟一个带 u/l 的行并以句点结尾。
    猜你喜欢
    • 1970-01-01
    • 2014-09-15
    • 2011-11-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-11-13
    相关资源
    最近更新 更多