我在测试我的NLP系统时发现了一些问题。我有一个java正则表达式"(.*\\.\\s*)*Dendryt.*"
和字符串"v Table of Contents List of Tables . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . "
它只是不停止计算。
很明显,这个正则表达式的复杂性很高,我会尝试重构它。对于我未来的正则表达式开发,你有什么建议吗?
感谢。
答案 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.*