$ echo '!abcae20' | grep -o -P '(?=.*\d)\w{4,}'
这将输出 nothing 。
但是以下工作:
$ echo '!abcae20' | grep -o -P '.*?(?=.*\d)\w{4,}'
!abcae20
有人可以给我一个解释吗?
答案 0 :(得分:2)
在您的第一个表达式中,前瞻断言将您的输入与(greedy
)匹配。
在正则表达式上运行调试测试会显示以下内容。
Matching REx "(?=.*\d)\w{4,}" against "!abcae20"
0 <> <!abcae20> | 1: IFMATCH[0](8)
0 <> <!abcae20> | 3: STAR(5)
REG_ANY can match 8 times out of 2147483647...
8 <!abcae20> <> | 5: DIGIT(6)
failed...
7 <!abcae2> <0> | 5: DIGIT(6)
8 <!abcae20> <> | 6: SUCCEED(0)
subpattern success...
0 <> <!abcae20> | 8: CURLY {4,32767}(11)
ALNUMU can match 0 times out of 2147483647...
failed...
Match failed
解释导致匹配失败的原因..
&LT;的 abcae20 强>&GT;!
The greedy quantifier first matches as much as possible.
So the .* here is matching the entire string.
&LT; !abcae20
&GT;
Then tries to match any numeric character following,
but there are no characters left to match.
&LT; !abcae2
的 0 强>&GT;
So it backtracks making the greedy match, match one less
character leaving the --> 0 <-- at the end unmatched.
&LT; !abcae20
&GT;
So it backtracks again matching one less leaving it unmatched.
&LT;的 abcae20 强>&GT;!
So it backtracks one more step matching one less again and failing your match.
正则表达式解释:
(?= look ahead to see if there is:
.* any character except \n (0 or more times)
\d digits (0-9)
) end of look-ahead
\w{4,} word characters (a-z, A-Z, 0-9, _) (at least 4 times)
你的第二个表达式确实与前面的非贪婪!
匹配.*?
,后跟你的前瞻断言匹配!abcae2
,然后回溯以匹配你的单词字符和完整的字符串。
正则表达式解释:
.*? any character except \n (0 or more times)
(?= look ahead to see if there is:
.* any character except \n (0 or more times)
\d digits (0-9)
) end of look-ahead
\w{4,} word characters (a-z, A-Z, 0-9, _) (at least 4 times)
答案 1 :(得分:2)
这有效:
echo '!abcae20' | grep -o -P '.*?(?=.*\d)\w{4,}
由于.*?
与!
匹配,(?=.*\d)
与abcae20
匹配,\w{4,}
与abcae20匹配。
在这一个:
echo '!abcae20' | grep -o -P '(?=.*\d)\w{4,}'
前瞻与!abcae20
匹配,贪婪。但是,\w{4,}
无法与!
匹配,因此失败。
以下是失败的perl regex调试输出:
Matching REx "(?=.*\d)\w{4,}" against "!abcae20"
0 <> <!abcae20> | 1:IFMATCH[0](8)
0 <> <!abcae20> | 3: STAR(5)
REG_ANY can match 8 times out of 2147483647...
8 <!abcae20> <> | 5: DIGIT(6)
failed...
7 <!abcae2> <0> | 5: DIGIT(6)
8 <!abcae20> <> | 6: SUCCEED(0)
subpattern success...
0 <> <!abcae20> | 8:CURLY {4,32767}(11)
ALNUM can match 0 times out of 2147483647...
failed...
答案 2 :(得分:1)
$ echo '!abcae20' | grep -o -P '(?=.*\d)\w{4,}'
在这个正则表达式中,前瞻(?=.*\d)
在字符串本身的开头捕获!abcae2
,因此将从字符串的开头尝试为\w{4,}
。但由于!
与\w
不匹配,因此完全匹配失败
可能跟随正则表达式会清除事情
$ echo '!abcae20' | grep -o -P '(?=\w*\d)\w{4,}'
abcae20
此处预测仅捕获abcae2
并且匹配从a
开始,因此生成的匹配abcae20
$ echo '!abcae20' | grep -o -P '.*?(?=.*\d)\w{4,}'
!abcae20
在上面的正则表达式中,您允许!
首先捕获.*?
,因此完全匹配。
答案 3 :(得分:1)
根据man pcrepattern
:
如果模式以
.*
或.{0,}
开头,并设置PCRE_DOTALL
选项(相当于Perl&#39; s/s
),从而允许点匹配对于新行,模式是隐式锚定,因为将对主题字符串中的每个字符位置尝试以下内容,因此在第一个之后的任何位置重试整个匹配都没有意义。< / p>
正如联机帮助页所提到的那样,如果.*
位于用作反向引用的括号内组中,则无法使用该优化,因为在这种情况下,可能有一点重试整个匹配一些后来的位置。相同的论点意味着在零长度前瞻的情况下,这种优化是不正确的,正如OP中的模式所提到的那样。
从联机帮助页中不清楚前瞻中的.*
是否会导致隐式锚点,但它肯定是可能的(尽管那可能是一个bug,imho)。无论出于何种原因,添加(?-s)
(我认为会关闭PCRE_DOTALL
)不会改变行为。但是,将.*
更改为其他内容。特别是,将其更改为[^\d]*
会使正则表达式具有预期的输出:
$ echo '!abcae20' | grep -P -o '(?=[^\d]*\d)\w{4,}'
abcae20
至少有趣的是,有些情况下前瞻断言显然没有创建隐式锚点,这可能会对上述分析产生一些疑问。但它可能只是与其他一些优化的互动。特别是,
$ echo '!abcae20' | grep -P -o '(?=.*\d)a'
a
$
如果模式被锚定,显然无法工作。另一方面,将a
更改为[ab]
,可能认为这不会改变匹配:
$ echo '!abcae20' | grep -P -o '(?=.*\d)[ab]'
$
(非常感谢@perreal对这个问题进行了精彩的讨论。)
最初让我认为这可能是一个错误的一些观察结果是:
$ echo '!abcde20' | grep -P -o '(?=.*\d)\w*'
abcde20
$ echo '!abcde20' | grep -P -o '(?=.*\d)\w+'
$ echo '!abcde20' | grep -P -o '(?=.*\d)\w'
$ echo '!abcde20' | grep -P -o '(?=.*\d)\w?'
a
b
c
d
e
2
0
这一切看起来都不合逻辑,但如果模式是隐式锚定的话,它实际上是有意义的。在第一个和最后一个案例(\w*
和\w
)中,模式将匹配输入开头的空字符串。然后grep -o
将在下一个字符位置重试该模式,并在该位置成功。在其他两种情况下(\w+
和\w
),锚定模式将失败,因此grep
将不会重试它。
尽管如此,我坚持认为隐式锚定(如果发生了这种情况)是一个错误,因为该联机帮助页很清楚它是优化并且优化不应该改变行为。 (此外,它与(?=.*\d)a
匹配不一致。)但是错误可能在文档中,因为 - 根据@perreal - Perl也拒绝这些匹配,并且pcre
的目标是与Perl兼容。
答案 4 :(得分:0)
原因:
(?=.*\d)\w{4,}
没有返回任何东西是因为第一部分:
(?=.*\d)
与整个表达相匹配,是一个积极的前瞻。正向前瞻是一种不返回其值的匹配。有关更好的说明,请参阅perldoc perlre