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

xpt*_*xpt 3 regex perl go

我发现非贪婪的正则表达式匹配在锚定到前面而不是结束时变得非贪婪:

$ 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
Run Code Online (Sandbox Code Playgroud)

现在看看这个,当锚定到最后:

$ 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?
Run Code Online (Sandbox Code Playgroud)

这是为什么?怎么不打印abc像以前一样?

(问题出现在我的Go代码中,但为了简单起见,在Perl中进行了说明).

ike*_*ami 7

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

非贪婪意味着它将匹配当前位置可能的最少字符,以使整个模式匹配.

在匹配a位置后0,bcabcab最少.*?可以匹配位置,1同时仍然满足模式的其余部分.

"abcabcabc" = /a.*?c$/ 详细地:

  1. 在pos 0处,a匹配1个char(a).
    1. 在pos 1,.*?匹配0个字符(空字符串).
      1. 在pos 1,c无法匹配.原路返回!
    2. 在pos 1,.*?匹配1 char(b).
      1. 在pos 2,c匹配1 char(c).
        1. 在pos 3,$无法匹配.原路返回!
    3. 在pos 1,.*?匹配2个字符(bc).
      1. 在pos 1,c无法匹配.原路返回!
    4. ...
    5. 在pos 1,.*?匹配7个字符(bcabcab).
      1. 在pos 8,c匹配1个char(c).
        1. 在pos 9,$匹配0个字符(空字符串).匹配成功!

"abcabcabc" = /a.*c$/ 详细(对比):

  1. 在pos 0处,a匹配1个char(a).
    1. 在pos 1,.*匹配8个字符(abcabcabc).
      1. 在pos 9,c无法匹配.原路返回!
    2. 在pos 1,.*匹配7个字符(abcabcab).
      1. 在pos 8,c匹配1个char(c).
        1. 在pos 9,$匹配0个字符(空字符串).匹配成功!

提示:避免使用具有两个非贪婪修饰符实例的模式.除非您将它们用作优化,否则它们很有可能匹配您不希望它们匹配的内容.这是与此有关,因为模式的隐式启动\G(?s:.*?)\K(除非处于领先抵消^,\A\G).

你想要的是以下之一:

/a[^a]*c$/
/a[^c]*c$/
/a[^ac]*c$/
Run Code Online (Sandbox Code Playgroud)

您还可以使用以下之一:

/a(?:(?!a).)c$/s
/a(?:(?!c).)c$/s
/a(?:(?!a|c).)c$/s
Run Code Online (Sandbox Code Playgroud)

在这种情况下使用后三者将是低效且不可读的,但是它们将使用长于一个字符的边界.