java问题中的正则表达式

时间:2010-05-10 14:00:50

标签: java regex

我在测试我的NLP系统时发现了一些问题。我有一个java正则表达式"(.*\\.\\s*)*Dendryt.*"和字符串"v Table of Contents List of Tables . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . "它只是不停止计算。

很明显,这个正则表达式的复杂性很高,我会尝试重构它。对于我未来的正则表达式开发,你有什么建议吗?

感谢。

3 个答案:

答案 0 :(得分:7)

您通过重复包含重复量词的组来运行catastrophic backtracking。随后的组合爆炸(给定足够的输入)会导致(tada!)堆栈溢出。

简化,你的正则表达式尝试

(.*\.\s*)匹配任意一系列字符包括点和空格,后跟一个点,后跟零个或多个空格,然后

(...)*重复这个次数。

Dendryt只有这样才会尝试匹配“Dendryt”。

由于此失败,引擎回溯,尝试不同的排列。可能性几乎无穷无尽......

为了说明,这里是RegexBuddy的regex调试器的屏幕截图,简化版本的数据:

RegexBuddy Screen Shot http://img714.imageshack.us/img714/3275/screen017.png

发动机在100万次排列后放弃。

你的正则表达式会更好一点(在将它转换为Java字符串时不要忘记转义反斜杠):

(.*)(\.\s*)*+Dendryt

在这种情况下,*+,一个所谓的占有量词,一旦匹配就会拒绝回溯。这样,正则表达式引擎可以更快地失败,但它仍然很糟糕,因为(.*)匹配任何,甚至是点。

([^.]*)(\.\s*)*+Dendryt

是安全的,除非您的数据可以在“虚线位”之前包含点。总而言之,请更清楚地说明您的要求,然后可以构建更好的正则表达式。

答案 1 :(得分:2)

试试这个:

"[^.]*+(?>\\.\\s*)*+Dendryt.*"

[^.]*+会消耗第一个点之前的所有内容,而+会生成* possessive,因此正则表达式永远不会回溯到该点。

(?>\\.\\s*)是一个atomic group:它匹配一个点和任何后续的空格,就好像它是一个单位一样。如果正则表达式引擎必须回溯到该点,它将跳过它直到组开始匹配的位置。

但它不会回溯到那一点,因为我也使该群体的量词占有欲。我想说明原子团的使用,但我本可以改为使用\\s*占有 - 或者两者兼而有之。

占有量词和原子组完全禁用回溯,但并不总是可以使用它们。当您必须允许回溯时,请将其保持在最低限度;不要让量词消耗超过它们所需的量。特别是,正如蒂姆所说,避免嵌套量词和量化的子表达式可以匹配相同的东西。

事实上,避免使用.*.+是一项很好的练习;它迫使你去思考它的机制。如果您没有想要匹配的具体内容,请考虑您想要匹配的内容,例如当我使用[^.]*代替第一个.*时你的正则表达式。

答案 2 :(得分:0)

正则表达式的这一部分 (?:.*\.\s*)*
说要获得尽可能多的具有
的连续行 如果是连续行,则在其中和末尾的文字点。这是必需的,直到最后一次找到
Dendryt 字面量。找到后,还要获取该行的其余部分。

这很容易使用一个简单的原子组 (?>_)
但是,由于是原子的,如果在组中使用.*,则不会停止
检查是否找到了最终的 Dendryt,因为它不会返回。

解决方案是使用惰性量词 .*? 代替。然后原子组将
允许检查最后一个 Dendryt

请注意,我不确定您的真实意图是什么,只是使用正则表达式 提供说明它目前是如何工作的,并提供不会的等价物 导致回溯问题(我看到的是)。

解决方案:

(?>.*?\.\s*)*Dendryt.*

https://regex101.com/r/D82egc/1