非贪婪匹配的正则表达式行为不同

3

我发现非贪婪正则表达式只有在锚定到前面时才变得非贪婪,而不是锚定到末尾:

$ echo abcabcabc | perl -ne 'print $1 if /^(a.*c)/'
abcabcabc
# OK, greedy match

$ echo abcabcabc | perl -ne 'print $1 if /^(a.*?c)/'
abc
# YES! non-greedy match

现在看一下,当锚定到末尾时:

$ echo abcabcabc | perl -ne 'print $1 if /(a.*c)$/'
abcabcabc
# OK, greedy match

$ echo abcabcabc | perl -ne 'print $1 if /(a.*?c)$/'
abcabcabc
# what, non-greedy become greedy?

为什么会这样?为什么它不像以前一样打印出abc
(这个问题是在我的Go代码中发现的,但为了简单起见,在Perl中说明)。

/(a.c*?)$/ 匹配 'abcabcabc' 中的最后一个 'abc'。由于您将锚定到结尾,因此应将 c 设为非贪婪模式。 - Aditya J.
@AdityaJ.,不,你已经改变了“算法”。对于你的“解决方案”,即使没有使用 *? ,也就是 /(a.c)$/ ,它仍然可以工作。但还是谢谢你尝试了。 - xpt
使用 .*? 正则表达式引擎开始匹配量词允许的最小字符数,这个数字是 。然后引擎继续前进并尝试下一个标记。这次匹配失败了,所以引擎回溯并扩展其匹配。这个过程不断重复——正则表达式引擎前进、失败、回溯、再次扩展其匹配、前进、失败...... 这是否定 /a[^a]*c$/ 的情况。 - hwnd
1个回答

7
$ echo abcabcabc | perl -ne 'print $1 if /(a.*?c)$/'
abcabcabc
# what, non-greedy become greedy?
非贪婪模式意味着它将在当前位置匹配尽可能少的字符,以使整个模式匹配。
在位置0匹配a后,在位置1,bcabcab是.*?可以匹配的最少字符,同时仍然满足其余模式。
"/a.*?c$/"对"abcabcabc"的详细解释:
1.在位置0,a匹配1个字符(a)。
2.在位置1,.*?匹配0个字符(空字符串)。
3.在位置1,c无法匹配。回溯!
4.在位置1,.*?匹配1个字符(b)。
5.在位置2,c匹配1个字符(c)。
6.在位置3,$无法匹配。回溯!
7.在位置1,.*?匹配2个字符(bc)。
8.在位置1,c无法匹配。回溯!
9. ...
10.在位置1,.*?匹配7个字符(bcabcab)。
11.在位置8,c匹配1个字符(c)。
12.在位置9,$匹配0个字符(空字符串)。匹配成功!
"/a.*c$/"对"abcabcabc"的详细解释(作为对比):
1.在位置0,a匹配1个字符(a)。
2.在位置1,.*匹配8个字符(abcabcabc)。
3.在位置9,c无法匹配。回溯!
4.在位置1,.*匹配7个字符(abcabcab)。
5.在位置8,c匹配1个字符(c)。
6.在位置9,$匹配0个字符(空字符串)。匹配成功!
提示:避免使用两个非贪婪修饰符的模式。除非您将它们用作优化,否则很有可能会匹配到您不想匹配的内容。这在这里是相关的,因为模式隐式地以\G(?s:.*?)\K开头(除非被前导的^\A\G取消)。
您需要的是以下之一:
/a[^a]*c$/
/a[^c]*c$/
/a[^ac]*c$/

您也可以使用以下其中之一:
/a(?:(?!a).)c$/s
/a(?:(?!c).)c$/s
/a(?:(?!a|c).)c$/s

使用后三种方法在这种情况下效率低下且难以阅读,但对于长于一个字符的边界它们仍然可用。

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