<table((?!</table>).)*</table>
匹配所有我的表格标签。然而,
<table(.(?!</table>))*</table>
不行。如果我试着用语言文字表达这个表达式,第二个似乎是有意义的,但我无法理解第一个。
它们的区别是什么?
参考资料:我从这里获得了“Tempered Greedy Token”这个术语:Tempered Greedy Token Solution
<table((?!</table>).)*</table>
匹配所有我的表格标签。然而,
<table(.(?!</table>))*</table>
不行。如果我试着用语言文字表达这个表达式,第二个似乎是有意义的,但我无法理解第一个。
它们的区别是什么?
参考资料:我从这里获得了“Tempered Greedy Token”这个术语:Tempered Greedy Token Solution
rexegg.com tempered greedy token 的参考资料相当简洁:
在
(?:(?!{END}).)*
中,*
量词应用于点,但现在它是一个tempered点。负的前瞻性(?!{END})
断言跟随当前位置的内容不是字符串{END}
。因此,点不能匹配{END}
的大括号,保证我们不会跳过{END}
分隔符。
就是这样: "tempered greedy token" 是一种适用于字符序列的否定字符类(与适用于单个字符的否定字符类相对比)。
注意:具有限制贪婪符号的正则表达式和带否定字符类的正则表达式之间的区别在于前者不会实际匹配除了序列本身之外的文本,而是匹配以该序列开头的单个字符。即(?:(?!abc|xyz).)+
不会在defabc
中匹配def
,但会匹配def
和bc
,因为a
开始了被禁止的abc
序列,而bc
没有。
它由以下部分组成:
(?:...)*
- 一个量化的非捕获组(它可以是一个捕获组,但捕获每个单独字符没有意义)(*
可以是+
,这取决于是否期望匹配空字符串)(?!...)
- 一个负向前瞻,实际上对当前位置右侧的值施加限制.
- (或任何(通常单个)字符)一个消耗型模式。然而,我们总是可以进一步通过在负向前瞻中使用交替项(例如 (?!{(?:END|START|MID)})
)或通过用否定字符类替换全匹配点(例如 (?:(?!START|END|MID)[^<>])
当尝试仅匹配标签内文本时)来调整令牌。
(.(?!</table>))*
首先匹配任何字符(但在没有DOTALL修饰符的情况下不包括换行符),然后检查它是否不跟随 </table>
,导致无法匹配 <table>table</table>
中的 e
。*消耗部分(.
)必须放置在温和的前瞻之后。
Rexegg.com 提供了一个思路:
{START}(?:(?!{(?:MID|RESTART)}).)*?{END}
<table>.*?chair.*?</table>
,我们会使用类似于 <table>(?:(?!chair|</?table>).)*chair(?:(?!<table>).)*</table>
)。abc 1 abc 2 xyz
获取abc 2 xyz
时,懒惰匹配是无法帮助您的(请参见abc.*?xyz
和abc(?:(?!abc).)*?xyz
)。温和贪婪模式消耗资源,因为在匹配消耗模式的每个字符后,都会执行一个前瞻检查。展开循环技术可以显著提高温和贪婪模式的性能。
假设我们要在 abc 1 abc 2 xyz 3 xyz 中匹配 abc 2 xyz
。与其使用 abc(?:(?!abc|xyz).)*xyz
检查 abc
和 xyz
之间的每个字符,我们可以使用 [^ax]*
跳过所有不是 a
或 x
的字符,然后匹配所有不带有 bc
后缀的 a
(使用 a(?!bc)
)以及所有不带有 yz
后缀的 x
(使用 x(?!yz)
):abc[^ax]*(?:a(?!bc)[^ax]*|x(?!yz)[^ax]*)*xyz
。
((?!</table>).)*
会检查要匹配的特定字符是否不是字符串</table>
的起始字符。如果是,则仅匹配该特定字符。*
重复零次或多次。
(.(?!</table>))*
仅在其后没有</table>
时,零次或多次匹配任何字符。因此,这将匹配表标记内的所有字符,除了最后一个字符,因为最后一个字符后面跟着</table>
。以下模式</table>
断言必须在匹配结尾处有一个关闭表格标记。这使得匹配失败。
请参见这里。
温和的贪婪令牌实际上意味着:
“匹配,但仅限于某一点”
如何实现:
在点
(匹配任何一个字符)
前面放置否定前瞻(?!notAllowedToMatch)
来表示您不希望匹配的标记,然后使用星号*
重复整个过程:
((?!notAllowedToMatch).)*
工作原理:
“看一下,吃掉一个”, 不断地从左到右移动一个字符, 直到看到不允许的序列(或字符串结束) ,此时匹配停止。
Wiktor的更详细的答案很好,但我认为需要一个更简单的解释。