繁体   English   中英

正则表达式进入无限循环

[英]regular expression goes into infinite loop

我正在解析(种类)表格的名称:

Parus Ater
H. sapiens
T. rex
Tyr. rex

通常有两个项(二项式),但有时有3个或更多。

Troglodytes troglodytes troglodytes 
E. rubecula sensu stricto

我写

[A-Z][a-z]*\.?\s+[a-z][a-z]+(\s*[a-z]+)*

大部分时间都有效,但偶尔会陷入无限循环。 花了一些时间来查找正则表达式匹配中的内容,然后我才意识到这是一个错字,我应该写

[A-Z][a-z]*\.?\s+[a-z][a-z]+(\s+[a-z]+)*

正确执行。

我的问题是:

  • 为什么会发生这种循环?
  • 有没有办法可以在运行程序之前检查类似的正则表达式错误? 否则,可能很难在prgram分发之前就捕获它们并引起问题。

[注意:我不需要物种的更一般的表达-物种名称有一个正式的100+行正则表达式规范-这只是一个初始过滤器]。

注意:之所以出现此问题,是因为尽管大多数名称被精确地提取为2个或偶数3/4个词(如斜体字),但仍有一些误报(例如"Homo sapiens lives in big cities like London" )并且匹配失败在“ L”处。]

注意:在调试此程序时,我发现正则表达式经常完成但非常慢(例如,在较短的目标字符串上)。 通过病理案例发现此错误很有价值。 我学到了重要的一课!

为了解决问题的第一部分,您应该阅读灾难性的回溯 本质上,正在发生的事情是有太多方法可以将您的正则表达式与您的字符串进行匹配,并且解析器会不断回溯以尝试使其正常工作。

在您的情况下,可能是嵌套的重新放置: (\\s*[az]+)*可能导致了一些非常非常奇怪的循环。 正如Qtax熟练地指出的那样,没有更多的信息就很难分辨。

不幸的是,您问题的第二部分不可能回答。 基本上是停止问题 由于正则表达式本质上是输入是字符串的有限状态机,因此您无法创建一个通用的解决方案来预测哪些正则表达式将发生灾难性的回退,而哪些不会。

至于使您的正则表达式运行更快的一些技巧? 那是一大罐蠕虫。 我花了很多时间独自研究正则表达式,并花了一些时间对其进行优化,以下是我发现的一般帮助:

  1. 如果您的语言支持,则在循环之外编译正则表达式。
  2. 只要有可能,就添加锚点 尤其是字符串开头的^ 另请参阅: 字边界
  3. 避免像瘟疫一样嵌套重复。 如果必须(必须这样做),请尽力为引擎提供提示,以使所有意外回溯短路。
  4. 利用风味构造来加快处理速度。 我偏爱非捕获组所有格限定词 它们不会出现在每种口味中,但是当它们出现时,应该使用它们。 还要检查原子组
  5. 我总是发现这是对的: 正则表达式获取的时间越长,使它变得高效的麻烦就越大。 正则表达式是一个强大而强大的工具,就像一个超级聪明的锤子。 不要陷入把一切视为钉子的陷阱 有时,您要查找的字符串函数就在您的鼻子下面。

希望这对您有所帮助。 祝好运。

对于第一个正则表达式:

[A-Z][a-z]*\.?\s+[a-z][a-z]+(\s*[a-z]+)*

如评论中指出的,灾难性的回溯是由于(\\s*[az]+)*而发生的。 但是,只有在使用String.matches()验证字符串时,它才成立,因为这是遇到无效字符导致引擎尝试回溯而不返回匹配( Matcher循环)的唯一情况。

让我们将无效字符串与(\\s*[az]+)*匹配:

inputstringinputstring;

(Repetition 1)
\s*=(empty)
[a-z]+=inputstringinputstring
FAILED

Backtrack [a-z]+=inputstringinputstrin
(Repetition 2)
\s*=(empty)
[a-z]+=g
FAILED

(End repetition 2 since all choices are exhausted)
Backtrack [a-z]+=inputstringinputstri
(Repetition 2)
\s*=(empty)
[a-z]+=ng
FAILED

Backtrack [a-z]+=n
(Repetition 3)
\s*(empty)
[a-z]+=g
FAILED

(End repetition 3 since all choices are exhausted)
(End repetition 2 since all choices are exhausted)
Backtrack [a-z]+=inputstringinputstr

现在,您应该已经注意到了问题。 让我们将T(n)定义为检查长度为n的字符串与模式不匹配的工作量。 从回溯的方法中,我们知道T(n) = Sum [i = 0..(n-1)] T(i) 由此,我们可以得出T(n + 1) = 2T(n) ,这意味着回溯过程的时间复杂度是指数级的!

*更改为+ 可以完全避免问题 ,因为重复的实例只能从空格字符和英文字母字符之间的边界开始。 相反,第一个正则表达式允许重复实例在任意2个字母字符之间开始。

为了说明这一点, (\\s+[az]+\\s*)*将使您回溯到无效输入字符串包含许多单词时,这些单词被多个连续的空格分隔开,因为它允许重复实例的多个位置开始。 这遵循公式b^d ,其中b是连续空格的最大数量(负1),而d是空格序列的数量。 它不如您拥有的第一个正则表达式严重(它需要至少一个Englsh字母和每个重复一个空格字符,而不是您的第一个正则表达式中每个重复一个英文字母),但这仍然是一个问题。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM