关于问号“懒惰”模式的正则表达式

时间:2014-09-08 16:13:19

标签: regex regex-greedy non-greedy

我理解?标记在这里意味着"懒惰"。

我的问题主要是[0-9]{2}? vs [0-9]{2}

它们是否相同?
如果是这样,我们为什么要编写前面的表达式? Aren的懒惰模式表现得更加昂贵吗? 如果没有,你能说出不同之处吗?

2 个答案:

答案 0 :(得分:7)

什么是“懒惰”(不情愿)匹配?

与正则表达式匹配时,默认情况下指针是 greedy

Left | Right
\d+    12345
^      ^
\d+    12345
  ^    ^^^^^ Matched!

懒惰与贪婪相反:

Left | Right
\d+?   12345
^      ^
\d+?   12345
  ^^    ^
       12345
         ^
       12345
          ^
       12345
           ^ Matched!

为什么重要?

在匹配中,量词* + ?默认为贪婪。这可能会导致不必要的行为,尤其是当我们希望某些字符仅在匹配完成时匹配时才匹配,否则会省略。

一个典型的例子是我们想要匹配单个XML标记:我们将使用<.*>将其失败。

Left | Right
<.*>   <p>hi</p><br /><p>bye</p>
^      ^
<.*>   <p>hi</p><br /><p>bye</p>
 ^^     ^^^^^^^^^^^^^^^^^^^^^^^^
<.*>   <p>hi</p><br /><p>bye</p>
   ^                           < [backtrack!]
<.*>   <p>hi</p><br /><p>bye</p>
   ^                           ^ Matched "<p>hi</p><br /><p>bye</p>"!
Left* | Right
<.*?>   <p>hi</p><br /><p>bye</p>
^       ^
<.*?>   <p>hi</p><br /><p>bye</p>
 ^^^     ^ [can we stop? we're lazy [yes]]
<.*?>   <p>hi</p><br /><p>bye</p>
    ^     ^ Matched "<p>"!

我可以量化什么作为懒惰?

您可以在量词和范围后添加?构造:

+(一个或多个),*(零或更多),?(可选);
{n,m}(n和m之间,n {n,}(n或更多),{n}(正好n次)。
(示例中的n和m是实数,满足n,m∈ N

  1. 不情愿的量词不愿继续推进。
    在考虑到引擎仅尝试匹配匹配绝对必要以使剩下的其余部分成功时,允许匹配尽可能少或尽可能少。请参阅以下案例:

    Left | Right
    abc*   abccccd
    ^      ^
    abc*   abccccd
     ^      ^
    abc*   abccccd
      ^      ^
    abc*   abccccd
      ^^     ^^^^ Matched "abcccc"!
    
    Left* | Right
    abc*?   abccccd
    ^       ^
    abc*?   abccccd
     ^       ^
    abc*?   abccccd
      ^^^     ^ [must we do this? we're lazy [no]]
               Matched "ab"!
    

    如图所示,它们尽可能匹配。

  2. 不情愿的量词放弃了娱乐其他量词。
    (演示目的;如果有人问,我做了告诉你可以像这样使用RegExp。)

    Left | Right
    c+c+   abccccd
    ^        ^
    c+c+   abccccd
    ^^       ^^^^
    c+c+   abccccd
      ^         < [backtrack]
    c+c+   abccccd
      ^^        ^ Matched "cccc"!
                  (c+ -> @ccc; c+ -> @c)
    
    Left* | Right
    c+?c+   abccccd
    ^         ^
    c+?c+   abccccd
    ^^^       ^ [pass]
    c+?c+   abccccd
       ^^      ^^^ Matched "cccc"!
                   (c+? -> @c; c+ -> @c)
    
  3. 确切范围量词不受影响。
    X{n}X{n}?之间,几乎没有差异;大多数引擎在内部优化了不情愿的旗帜。这是因为惰性构造仅在匹配是动态时才适用,其中引擎可以对量词(需要或贪婪)采用一种方式或另一种方式,但不适用于这种情况。

  4. 查看regex101,一个功能完善的正则表达式引擎,附带说明和调试器日志,以显示指针步骤。 另请阅读The Stack Overflow Regex Reference

答案 1 :(得分:3)

[0-9]{2}[0-9]{2}?之间存在差异。

贪婪匹配和懒惰匹配(添加?)之间的区别与回溯有关。正则表达式引擎构建为匹配文本(从左到右)。因此,当您要求表达式匹配一系列字符时,它是合乎逻辑的,它会尽可能多地匹配。


假设我们有字符串acac123

如果我们使用[a-z]+c+代表1次重复或{1,})的贪婪匹配:

  • [a-z]+acac匹配,在1
  • 时失败
  • 然后我们会尝试匹配c,但在1
  • 时失败
  • 现在我们开始回溯,并成功匹配acac

如果我们使这个懒惰([a-z]+?c),我们将获得不同的响应(在情况下)并且效率更高:

  • [a-z]+?会匹配a,但请停止,因为它会看到下一个字符与表达式的其余部分匹配c
  • 然后c会匹配,成功匹配ac(没有回溯)

现在您可以看到X{#}X{#}?之间存在差异,因为{#}不是范围,甚至贪婪的匹配也不会体验任何回溯。 Lazily匹配通常与*(0+重复或{0,})或+一起使用,但也可以与范围{m,n}一起使用(其中n是可选的)。

当您想要匹配尽可能少的字符时,这是必不可少的,当您想要填充字符串{{1时.*?时,您通常会在表达式中看到foo.*?bar }})。然而,很多时候,懒惰匹配是坏/低效正则表达式的一个例子。很多人会像foo bar filler text bar那样匹配双引号内的所有内容,当你可以通过编写foo:"(.*?)"这样的表达式来匹配任何 {{1} }第


最后一点,foo:"([^"]+)"通常表示“可选”或匹配"次。 ?只会在范围({0,1}?{m,n}或其他*)上使用时才会使匹配变得懒惰。这意味着+不会使?变得懒惰(因为我们已经说X?毫无意义),但它将是可选的。但是,你可以做一个懒惰的“可选”匹配:X将懒惰地匹配0-1次。