如何使用否定的字符类在内部工作(没有回溯)?

时间:2014-05-14 18:32:40

标签: regex perl

我读到要停止正则表达式的回溯,可以使用否定的字符类。就像我们希望匹配<Em>中的This <Em>is the shiz <Em>一样,我们可以使用比<[^>]+>更快的<.+?>,因为后者在每个字符之后回溯,但前者不会回溯到所有。
有人可以解释&lt; [^&gt;] +&gt;内部匹配?

4 个答案:

答案 0 :(得分:3)

你必须先了解贪婪和懒惰的量词是如何工作的。

如果后面的子模式(在您的示例中为>)匹配,则惰性量词将测试每个字符。贪心量词将采用所有可能的字符,并且只有在需要之后才会回溯以使下一个子模式(在您的示例中为>)匹配。

但是如果不使用点(除了换行符之外的所有符号),你使用的是一个不包含字符>的否定字符类,你肯定没有回溯的步骤。我希望能够清楚!

为了说明我的意见,建议您使用http://regex101.com的调试器尝试这三种模式:<.*><.*?><[^>]*> 使用此字符串:<abcd efgh="ijkl" mnop="qrst"> lapin

特别关于PCRE:PCRE lib默认使用自动内部优化进行编译。如果使用否定字符类后跟排除字符,则量词会自动转换为占有量词。此功能只能在编译时更改。 (source)。它可能与Perl相同。

答案 1 :(得分:2)

.+?>之间匹配路径的主要区别在于,为了匹配当前字符, .+?>必须查看以下字符,其中[^>]+为不即可。 [^>]+表示&#34;匹配一个或多个不是>的字符......它只会吃掉它们而不再考虑它们。

为什么.+?>需要向前看并导致回溯?

相比之下,在每一步中,.+?>向前迈出一步然后退一步。为什么?

我们假设您尝试使用thing>匹配.+?>。在第一步,在t前面,因为?是懒惰的, .+?>中的点匹配零个字符。然后引擎前进到下一个角色。在那里,尝试匹配>,但未通过。因此引擎回溯,然后懒惰的量词从沙发上下来并允许点匹配。对h,i,n和g重复该过程:对于每个字符,惰性点首先匹配零个字符;然后引擎尝试匹配>,失败,回溯并匹配该字母。

这在RegexBuddy调试器中清楚地显示,其中RB尝试使用thing>匹配.+?>

RegexBuddy debugger

将此与此屏幕截图进行比较,其中RB尝试使用thing>

匹配[^>]+>

RB debugger

答案 2 :(得分:0)

  

有人可以解释一下&lt; [^&gt;] +&gt;内部匹配?

实际上,这是更容易理解的。它说:

Match the sequence
1) one   '<'
2) at least one (as many as posible) '[^>]'  (i.e.: any character except for '>')
3) one '>'

然后,在This <Em>is the shiz <Em>搜索的匹配器将完成预期的操作。从头开始,它将(暂时)匹配第一个<,然后转到第2部分,然后匹配Em然后转到第3部分... ...并找到{{1} }:完成!

使用>指令是:

<.*?>

现在,从头开始,它将(暂时)与第一个Match the sequence 1) one '<' 2) any amount (least possible) of '.' (i.e.: any character) 3) one '>' 匹配,然后转到第2部分,并且(这里是第一个区别)它首先会说,“嘿,我找到了一个空字符串(<<之间的那个,它与第2步匹配,让我们进入第3步“。但是它会失败(“我发现了一个E,我期待一个E;然后它会回到模式序列中(但不是在输入序列中!我不会称之为回溯,只是向前看分支,好吧,让我们尝试匹配>进行步骤2)(它匹配,继续......)它会再次失败......但是在下一次重复中它会成功。 所以这在功能上是等效的,效率稍差。

请注意,如果模式没有在那里结束,那么替代方案在功能上并不相同。例如,如果模式是:E并且我给它输入<.*?><br>它将匹配hi <em>hello ! <em><br>bye,这不是您可能想要的。

答案 3 :(得分:0)

a+会保持匹配的字符,直到达到不是a的字符。

[a-z]+会保持匹配字符,直到达到不是az或其中之间的字符。

[^a-z]+保持匹配字符,直到达到 az或其中之间的某个字符。对于匹配器,否定字符类和常规字符类的工作方式完全相同;只是在正则表达式构造时,反转列表被翻转,以便不仅仅匹配指定范围内的 ,它只匹配指定范围内的