为什么这个正则表达式会杀死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中子表达式匹配的任何内容的起始位置和结束位置,以防它需要回溯。 即使它是非捕获组,也是如此,即,
(?:.)+
…但由于它是一个捕获组,因此必须保存更多信息。 一次完成一个角色的所有这一切变得非常昂贵。 将带括号的组内的单个字符与组上的*
或+
量词匹配几乎是不正确的。 此外,只有在需要捕获某些内容时才应使用捕获组; 否则,使用非捕获品种。