假设我有一个这样的字符串:
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
。在回溯中不考虑负向前看。这种负面看法有什么问题?
答案 0 :(得分:3)
注意: - 我会解释这个我自己的理解。欢迎所有更正
为什么会发生这种情况?
默认情况下, 正则表达式引擎的目标是满足在字符串中找到匹配所需的所有条件 。如果当前状态无法满足正则表达式条件,则通过回溯,简单匹配和跳转到不同的已保存状态(通常由NFA引擎支持)来实现。
一旦满足所有条件,就满足要求并且引擎停止检查任何其他事情。由于已满足要求,因此不需要回溯,匹配或做任何花哨的事情。
现在回到你的问题,以下是你的字符串
121456 word123word456word897 10:10
在你的第一个正则表达式
\d+.+(?=10:10)
i)
\d+
匹配所有数字< - No Problemii)由于
.+
贪婪,它将匹配所有字符串并移至最后< - 没问题iii)为了满足下一个条件
(?=10.10)
,没有剩下的字符串。 所以所有条件都没有实现,因此符合这个条件,正则表达式 引擎开始回溯直到找到10:10
在你的第二个正则表达式
\d+.+(?!10:10)
i,ii)前两个步骤与上面完全相同
iii)为了满足下一个条件
(?!10:10)
,无论如何(这里,我们已经达到了结束) 由于$
的贪婪而导致的字符串或.+
不应与10:10
匹配。很明显,字符串的结尾 不匹配10:10
。因此,我们所有的条件都得到了满足。所以 因为我们所有人都不需要回溯或做任何事情 符合要求的条件。
一张图片胜过千言万语
For \d+.+(?=10:10)
For \d+.+(?!10:10)
图片余额: - 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}}。引擎不会回溯,因为它不需要。该模式已成功匹配。