2

我理解?这里的标记意味着“懒惰”。

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

他们是一样的吗?
如果是这样,我们为什么要写前一个表达式?惰性模式不是更昂贵的性能吗?
如果不是,你能分辨出区别吗?

4

2 回答 2

8

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

与正则表达式匹配时,指针默认为贪心

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

懒惰是贪婪的反面:

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

为什么这有关系?

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

一个典型的例子是当我们想要匹配单个 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 < m),{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}?

查看regex101,这是一个完善的正则表达式引擎,它带有解释和调试器日志,向您显示指针步骤。另请阅读堆栈溢出正则表达式参考

于 2014-09-08T18:43:52.387 回答
5

和之间没有区别。[0-9]{2}[0-9]{2}?

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


假设我们有字符串acac123

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

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

如果我们使这个惰性([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:"([^"]+)"并匹配除s 之外 "的任何内容。


最后注,?通常表示“可选”或匹配{0,1}时间。仅当您在范围( , ,或其他)?上使用它时才会使匹配变得懒惰。这意味着不会变得懒惰(因为我们已经说过是没有意义的),而是它是可选的。但是,您可以进行惰性“可选”匹配:将惰性匹配 0-1 次。{m,n}*+?X?X{#}?[0-9]??

于 2014-09-08T18:38:06.083 回答