正则表达式:{n}和{n,m}忽略最大重复次数

3

我有一个关于正则表达式的问题,那就是{n}和{n, m}的最大重复次数。

$ man grep
...
Repetition
    A regular expression may be followed by one of several repetition operators:
...
    {n}    The preceding item is matched exactly n times.
    {n,}   The preceding item is matched n or more times.
    {,m}   The preceding item is matched at most m times.  This is a GNU extension.
    {n,m}  The preceding item is matched at least n times, but not more than m times.
...

现在考虑一个测试文件:
$ cat ./sample.txt
1
12
123
1234

然后使用grep命令查找重复出现了2次的数字[0-9]:

$ grep "[0-9]\{2\}" ./sample.txt
12
123
1234

为什么这个包括123和1234?

另外,我在相同的文本文件中使用grep查找至少重复2次但不超过3次的数字:

$ grep "[0-9]\{2,3\}" ./sample.txt
12
123
1234

为什么这会返回“1234”?

一个明显的解决方法是使用grep和反向grep来过滤掉过多的结果。例如,

$ grep "[0-9]\{2,\}" ./sample.txt | grep -v "[0-9]\{4,\}"
12
123

有人能帮我理解为什么{n}会返回包含重复n次的模式的行吗?而{n,m}为什么会返回重复m次的模式?


我认为所有的_grep_只需要在行中找到它就可以了。它不会匹配额外的数字。 - user557597
2个回答

6

如果你没有使用锚点,正则表达式可以匹配字符串中的任何位置。

$ grep "[0-9]\{2\}" ./sample.txt 将匹配包含2个数字的任何行。

使用 ^ 强制表达式从行首开始匹配,使用 $ 强制匹配到行尾。例如:

$ grep '^[0-9]\{2\}$' ./sample.txt
# Using single quotes to avoid potential substitution issues. Hat tip to @ghoti

这应该只返回12


1
实际上,只有在$后面的文本可能被解释为替换时,才需要转义$。例如,请尝试echo "$"。话虽如此,切换到单引号可能是一个更好的选择。 :-) - ghoti
谢谢 @ghoti / fish - 我正在使用 Fish shell,变得有点懒散了。 :) - Zak
关于锚点的想法对我来说很有道理,谢谢。我会使用反向grep来过滤结果,这样目标字符串就不需要出现在行的开头/结尾。 - Culip

1

一个模式可以在较长的文本中找到,也可以遵循完全相同的模式。对于grep使用-o选项以查看正则表达式找到匹配的位置。两个数字可以在由两个数字组成的数字中找到,也可以在长度为10位的数字中找到。

另一个答案提到了两个锚点,但有一个单词边界标记\b,如果使用,则匹配边界位置。这将关闭两端。不幸的是,POSIX BRE(grep默认的正则表达式风格)不支持此功能,但在GNU sed中,您可以启用Perl正则表达式并进行测试:

grep -P '\b[0-9]{2}\b' file

仅使用 grep,两个符号 \<\> 在同一位置都能匹配:

grep '\<[0-9]\{2\}\>' file

POSIX也有单词边界。[[:<:]][0-9]\{2\}[[:>:]] - ghoti
@ghoti 是的,但是我用grep无法让它工作起来,而且我也不确定,所以没有参考它。 - revo
啊,有趣。在macOS上对我有效,但在FreeBSD上无效。而快捷键\<\>在两者中都有效。我没有深入研究的意愿,所以我马上就会删除我的评论。 :-) - ghoti
1
@ghoti 在这里留下你的评论。也许其他人也会指向它。 - revo

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接