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

KTr*_*ver 0 regex non-greedy regex-greedy

我明白?这里的标记意味着"懒惰".

我的问题基本上是[0-9]{2}?vs[0-9]{2}

它们一样吗?
如果是这样,我们为什么要编写前面的表达式?是不是懒惰模式更加昂贵的表现明智?
如果没有,你能分辨出来吗?

Uni*_*ron 7

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

与正则表达式匹配时,默认情况下指针是贪心的:

Left | Right
\d+    12345
^      ^
\d+    12345
  ^    ^^^^^ Matched!
Run Code Online (Sandbox Code Playgroud)

懒惰是贪婪的反面:

Left | Right
\d+?   12345
^      ^
\d+?   12345
  ^^    ^
       12345
         ^
       12345
          ^
       12345
           ^ Matched!
Run Code Online (Sandbox Code Playgroud)

为什么这有关系?

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

一个典型的例子是当我们想要匹配单个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>"!
Run Code Online (Sandbox Code Playgroud)
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>"!
Run Code Online (Sandbox Code Playgroud)

我可以量化什么懒惰?

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

+(一个或多个),*(零或更多),?(可选);
{n,m}(在n和m之间,其中n <m),{n,}(n或更多),{n}(恰好n次).
(n和m在实施例中是实数,并且满足N,Mε Ñ)

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

    Left | Right
    abc*   abccccd
    ^      ^
    abc*   abccccd
     ^      ^
    abc*   abccccd
      ^      ^
    abc*   abccccd
      ^^     ^^^^ Matched "abcccc"!
    
    Run Code Online (Sandbox Code Playgroud)
    Left* | Right
    abc*?   abccccd
    ^       ^
    abc*?   abccccd
     ^       ^
    abc*?   abccccd
      ^^^     ^ [must we do this? we're lazy [no]]
               Matched "ab"!
    
    Run Code Online (Sandbox Code Playgroud)

    如图所示,它们尽可能少地匹配.

  2.     不情愿的量词放弃了娱乐其他量词.
    (演示目的;如果有人问,我也不会告诉你它是确定使用正则表达式是这样.)

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

查看regex101,这是一个运行良好的正则表达式引擎,附带说明和调试器日志,向您显示指针步骤.另请参阅Stack Overflow Regex参考!

  • 好的工作实际上写出了内部指针的位置.我想我们解释了类似的观点,但我跳过了扩展的例子. (2认同)

Sam*_*Sam 5

有没有之间的差异[0-9]{2}和[0-9]{2}?。

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


假设我们有字符串acac123。

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

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

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

  • [a-z]+?将匹配a,但停止,因为它看到下一个字符匹配表达式的其余部分c
  • 在c随后将匹配,匹配成功a和c(无回溯)

现在你可以看到和之间没有区别,因为不是一个范围,即使是贪婪的匹配也不会经历任何回溯。惰性匹配通常与(0+ 重复或) 或一起使用,但也可以与范围一起使用(其中是可选的)。X{#}X{#}?{#}*{0,}+{m,n}n

当您想要匹配尽可能少的字符时,这是必不可少的,并且.*?当您想要填充一些空间(foo.*?bar在字符串上foo bar filler text bar)时,您经常会在表达式中看到。然而,很多时候惰性匹配是错误/低效的正则表达式的一个例子。很多人会做这样的事情foo:"(.*?)"来匹配双引号内的一切时,你可以通过写你的表达就像避免懒散的比赛foo:"([^"]+)"和比赛什么,但 "秒。


最后说明,?通常表示“可选”或匹配{0,1}时间。?不仅会让比赛懒,如果你使用它在一个范围({m,n},*,+,或其他?)。这意味着X?不会使X懒惰(因为我们已经说过{#}?是毫无意义的),而是它是可选的。但是,您可以进行惰性“可选”匹配:[0-9]??将惰性匹配 0-1 次。