我试图理解正则表达式中的展开循环。有什么区别:
MINISTÉRIO[\s\S]*?PÁG
和
MINISTÉRIO(?:[^P]*(?:P(?!ÁG\s:\s\d+\/\d+)[^P]*)(?:[\s\S]*?))PÁG
在此背景下:
为什么我要使用第二个,如果第一个做同样的事情?
感谢。
答案 0 :(得分:4)
请参阅此Unroll the loop technique来源:
此优化技术用于优化形式
(expr1|expr2|...)*
的重复交替。这些表达并不罕见,并且在交替中使用另一次重复也可能导致超线性匹配。超线性匹配来自不确定性表达式(a*)*
。展开循环技术是基于这样的假设:在大多数情况下,你在重复的交替中,这种情况应该是最常见的,哪一种是例外的。我们将调用第一个,正常情况和第二个,特殊情况。然后,展开循环技术的一般语法可以写成:
正常*(特殊正常*)*
所以,这是一个优化技术,其中交替变成线性匹配的原子。
这使得这些展开的模式非常有效,因为它们涉及较少的回溯。
您的MINISTÉRIO[\s\S]*?PÁG
是未展开的模式,MINISTÉRIO[^P]*(?:P(?!ÁG)[^P]*)*PÁG
是。查看演示(两者都保存了PCRE选项以显示上面框中的步骤数。正则表达式引擎的正则表达式性能不同,但这将告诉您确切的性能差异)。在text
之后添加更多文字:第一个正则表达式将开始需要更多步骤才能完成,第二个正则表示只会在添加P
后显示更多步骤。因此,在已知部分中使用的字符不常见的文本中,展开的模式非常有效。
请参阅我的回答中的Difference between .*?
, .*
and [^"]*+
quantifiers部分,了解延迟匹配的工作原理([\s\S]*?
与.*?
相同,其中DOTALL修饰符的语言允许.
也匹配换行符。)
延迟匹配模式总是缓慢且低效吗?并非总是如此。对于非常短的字符串,惰性点匹配通常更好(1-10个符号)。当我们谈论长输入时,可能存在前导分隔符,而没有尾随分隔符,这可能导致过度回溯导致超时问题。
如果您有任意长度的输入且可能无法匹配,请使用展开的模式。
在您的输入受到控制时使用延迟匹配,您知道总会有匹配,某些已知的设置日志格式等。
常规字符串文字("String\u0020:\"text\""
):"[^"\\]*(?:\\.[^"\\]*)*"
多行注释正则表达式{/* Comments */
):/\*[^*]*\*+(?:[^/*][^*]*\*+)*/
@<...>@
评论正则表达式:@<[^>]*(?:>[^@]*)*@