为什么这个正则表达式会杀死Java正则表达式引擎?

我有这个天真的正则表达式“<([\ s] | [^ ”(不包括引号)。 它看起来很简单,但它对下面的HTML文本起作用时确实是邪恶的。 它将Java正则表达式引擎发送到无限循环。

我有另一个正则表达式(“”),这有点相同,但它不会杀死任何东西。 你知道为什么会这样吗?

 var numDivs, layerName; layerName = "lnavLayer"; catLinkName = "category"; numDivs = 2; function toggleLayer(layerID){ if (!(navigator.appName == "Netscape" && navigator.appVersion.substr(0, 1) < 5)){ thisLayer = document.getElementById(layerName + layerID); categoryLink = document.getElementById(catLinkName + layerID); closeThem(); if (thisLayer.className == 'subnavDefault'){ thisLayer.className = 'subnavToggled'; categoryLink.className = 'leftnavLinkSelectedSection'; } } } function closeThem(){ for(x = 0; x   

它甚至可以使用在线Java正则表达式工具(例如www.fileformat.info/tool/regex.htm )或像RegexBuddy这样的实用程序进行循环 。

Java正则表达式引擎崩溃的原因是正则表达式的这部分导致堆栈溢出(确实!):

 [\s]|[^<] 

这里发生的是,与\ s匹配的每个字符也可以匹配[^ <]。 这意味着有两种方法可以匹配每个空白字符。 如果我们用A和B表示两个字符类:

 A|B 

然后可以将三个空格的串匹配为AAA,AAB,ABA,ABB,BAA,BAB,BBA或BBB。 换句话说,这部分正则表达式的复杂性是2 ^ N. 这会杀死任何没有任何保护措施的正则表达式引擎,而这种保护措施不会对我称之为灾难性的回溯 。

在正则表达式中使用交替(垂直条)时,请始终确保备选方案是互斥的。 也就是说,最多可以允许一个替代方案匹配任何给定的文本位。

正则表达式中的正则表达式([\s]|[^<])表示IS空白或IS不是<字符的任何单个字符,这是多余的,因为空白字符不是<字符。 在我看来,你真正的意思是:

 `"<([^<])+?>"` 

我不确定这是否会解决无限循环,但我想我会指出这一点。

另一个问题(除了Jan所说的)是你在括号内一次匹配一个字符,相当于这个简化的例子:

 (.)+ 

每次执行正则表达式的这一部分时,正则表达式引擎必须保存parens中子表达式匹配的任何内容的起始位置和结束位置,以防它需要回溯。 即使它是非捕获组,也是如此,即,

 (?:.)+ 

…但由于它是一个捕获组,因此必须保存更多信息。 一次完成一个角色的所有这一切变得非常昂贵。 将带括号的组内的单个字符与组上的*+量词匹配几乎是不正确的。 此外,只有在需要捕获某些内容时才应使用捕获组; 否则,使用非捕获品种。