为什么负向前看不要考虑回溯?

时间:2016-04-28 13:38:06

标签: regex perl

假设我有一个这样的字符串:

121456 word123word456word897 10:10

我的条件是检查字符串末尾的10:10。所以我将按如下方式编写模式:

$s = "121456    word123word456word897   10:10";
if($s =~m/\d+(.+)(?=10:10)/)
{
    print "$1\n";  #
    print "Hello ";
}

此处.+匹配结束,然后它会回溯以匹配10:10$1包含word123word456word897

但问题是,我写的条件是负向前看,但正则表达式搜索引擎无法回溯。

if($s =~m/\d+(.+)(?!10:10)/)
{
    print "$1\n";
    print "Hello ";
}

此处$1包含word123word456word897 10:10。在回溯中不考虑负向前看。这种负面看法有什么问题?

4 个答案:

答案 0 :(得分:3)

注意: - 我会解释这个我自己的理解。欢迎所有更正

为什么会发生这种情况?

默认情况下, 正则表达式引擎的目标是满足在字符串中找到匹配所需的所有条件 。如果当前状态无法满足正则表达式条件,则通过回溯,简单匹配和跳转到不同的已保存状态(通常由NFA引擎支持)来实现。

一旦满足所有条件,就满足要求并且引擎停止检查任何其他事情。由于已满足要求,因此不需要回溯,匹配或做任何花哨的事情。

现在回到你的问题,以下是你的字符串

121456    word123word456word897   10:10

在你的第一个正则表达式

\d+.+(?=10:10) 
  

i)\d+匹配所有数字< - No Problem

     

ii)由于.+贪婪,它将匹配所有字符串并移至最后< -   没问题

     

iii)为了满足下一个条件(?=10.10),没有剩下的字符串。   所以所有条件都没有实现,因此符合这个条件,正则表达式   引擎开始回溯直到找到10:10

在你的第二个正则表达式

\d+.+(?!10:10) 
  

i,ii)前两个步骤与上面完全相同

     

iii)为了满足下一个条件(?!10:10),无论如何(这里,我们已经达到了结束)   由于$的贪婪而导致的字符串或.+不应与10:10匹配。很明显,字符串的结尾   不匹配10:10。因此,我们所有的条件都得到了满足。所以   因为我们所有人都不需要回溯或做任何事情   符合要求的条件。

一张图片胜过千言万语

For \d+.+(?=10:10)

enter image description here

For \d+.+(?!10:10)

enter image description here

图片余额: - https://regex101.com/

答案 1 :(得分:2)

默认情况下.+量词是贪婪的,因此表达式的.+部分将匹配字符串的结尾并断言前面没有10:10因此表达式将通过没有理由需要回溯。

具有正向前瞻的表达式也将到达字符串的末尾。区别在于前瞻断言将失败,表达式的.+部分将重新跟踪,直到断言通过或没有更多的查询分支。

答案 2 :(得分:2)

(?!10:10)已经过检查。

          1         2         3
0123456789012345678901234567890123456

121456 word123word456word897   10:10

\____/\____________________________/|
  \d+               .+              (?!10:10)

用语言说,

  • 整个模式匹配,因为
    • \d+匹配位置0的6个字符。
    • .+匹配位置6的30个字符。
    • (?!10:10)匹配位置36处的0个字符,因为
      • 10:10在第36位不匹配。

你真正想要的是

if (my $match = /( \d+ (?:(?!10:10).)* )/sx) {
   print("$match\n");
}

(?:(?!STRING).)*STRING[^CHAR]*CHAR

答案 3 :(得分:1)

正则表达式尝试很难匹配。如果有任何方法可以将您的输入与特定模式匹配,则正则表达式引擎会找到它。回溯仅发生在故障上 - 也就是说,必要时为了尝试替代方案以使模式匹配。环顾四周会引起回溯,但负面的环顾是很棘手的。他们的行为并不像许多人直觉所期望的那样。

您的两个示例案例都以相同的方式开始:

  • \d+匹配121456
  • .+与其他所有内容相匹配

对于正向前瞻性案例,当没有更多输入时(?=10:10)无法匹配,因此引擎会回溯直到可以。

对于您的负面预测案例,(?!10:10) 可以在输入结尾处匹配,因为接下来的事情是''(即什么都没有),什么都不是{{ 1}}。引擎不会回溯,因为它不需要。该模式已成功匹配。