Tempered Greedy Token – 在负面预测之前放置点有什么不同

<table((?!</table>).)*</table> 

匹配我所有的表标签,但是,

 <table(.(?!</table>))*</table> 

才不是。 第二个似乎是有道理的,如果我试图用文字写出expression,但我不能理解第一个。

有人可以向我解释这个区别吗?

作为参考,我从这里得到了“Tempered Greedy Token”这个词: http : //www.rexegg.com/regex-quantifiers.html#tempered_greed

由于Google在tempered greedy token的结果中回报了这个问题,所以我觉得有必要提供一个更全面的答案。

什么是脾气暴躁的令牌?

rexegg.com 脾气贪婪的标记引用是相当简洁的:

(?:(?!{END}).)**量词适用于一个点,但现在是一个回火点。 负向前瞻(?!{END})声明跟在当前位置之后的不是string{END} 。 因此,点不能匹配{END}的开始大括号,保证我们不会跳过{END}分隔符。

那就是: 脾气暴躁的标记是一种字符序列的 否定字符类 (比较单个字符的 否定字符类 )。

注意脾气暴躁的标记和否定的字符类之间区别在于 ,前者与序列本身以外的文本并不真正匹配,而是单个字符不会启动该序列。 即(?:(?!abc|xyz).)+defabc不会与defdefabc ,但会匹配def bc ,因为a启动被禁止的abc序列,而bc不会。

它包括:

  • (?:...)* – 一个量化的非捕获组(它可能是一个捕获组,但是捕获每个单独的字符是没有意义的)(a *可以是+ ,这取决于空string匹配是预期)
  • (?!...) – 实际上对当前位置右侧的值施加限制的负向预测
  • . – (或任何(通常是单个)字符)消费模式。

然而,我们总是可以通过使用反向预测(例如(?!{(?:END|START|MID)}) )中的交替或通过用否定的字符类replace全匹配的点来进一步调节令牌(例如(?:(?!START|END|MID)[^<>])当试图匹配只在标签内的文本)。

消费部分安置

请注意,没有提到消费部分(最初的脾气暴躁的代币中的点)被放置前瞻之前的构造。 Avinash的答案是清楚地解释这一部分: (.(?!</table>))*首先匹配任何字符(但没有DOTALL修饰符的换行符),然后检查是否没有遵循</table>导致失败匹配<table>table</table>中的e消费部分( . )必须放在回火预见之后

什么时候使用脾气暴躁的代币?

Rexegg.com给出了一个想法:

  • 当我们想在分隔符1和分隔符2之间匹配一个没有子串3(例如{START}(?:(?!{(?:MID|RESTART)}).)*?{END}
  • 当我们想要匹配一个包含特定模式的文本块而不溢出后续的块(例如,而不是像<table>.*?chair.*?</table>的懒点匹配)时,我们会使用类似<table>(?:(?!chair|</?table>).)*chair(?:(?!<table>).)*</table> )。
  • 当我们想要匹配2个string之间可能的最短窗口。 当你需要得到abc 2 xyz时,懒惰的匹配将无济于事abc 2 xyz abc 1 abc 2 xyz (见abc.*?xyzabc(?:(?!abc).)*?xyz )。

性能问题

贪婪的贪婪标记是耗费资源的,因为在每个与消费模式匹配的字符之后执行预先检查。 展开循环技术可以显着提高贪婪的贪婪标记性能。

比方说,我们想在abc 1 abc 2 xyz 3 xyz中匹配abc 2 xyz xyz 。 而不是使用abc(?:(?!abc|xyz).)*xyz检查abcxyz之间的每个字符abc(?:(?!abc|xyz).)*xyz ,我们可以用[^ax]*跳过所有不是ax字符,然后匹配所有没有跟随bc (带有a(?!bc) )和所有没有跟随yz (带有x(?!yz) )的x(?!yz)abc[^ax]*(?:a(?!bc)[^ax]*|x(?!yz)[^ax]*)*xyz

((?!</table>).)*将检查要匹配的特定字符不能是string</table>的起始字符。 如果是,那么只有它匹配那个特定的字符。 *重复相同的零次或多次。

(.(?!</table>))*只有在没有</table>才会匹配任何字符,零次或多次。 所以这将匹配表标签内的所有字符excpet最后一个字符,因为最后一个字符后面是</table> 。 下面的模式</table>声明在比赛结束时必须有一个closures表格标签。 这使比赛失败。

看到这里

温和的贪婪标记实际上意味着:

“匹配,但只能达到一个点”

你如何做到这一点:

你把你不想匹配的标记作为一个负面的前瞻(?!notAllowedToMatch)在一个点的前面. (匹配任何一件事),那么你重复整个事情与星*

((?!notAllowedToMatch).)*

怎么运行的:

“看,吃”一遍又一遍,通过inputstring从左到右移动一个字符 ,直到看到不允许的序列(或string的结束),此时匹配停止。

Wiktor的更详细的答案是好的,我只是想一个简单的解释是为了。