正则表达式模式灾难性回溯

时间:2015-04-20 14:50:41

标签: java regex backtracking

我的旧Java系统中使用了下面显示的正则表达式,最近导致了回溯问题。 回溯线程经常导致机器的CPU达到上限,并且在重新启动应用程序之前它不会返回。

有没有人建议更好的方法来重写这种模式或一种可以帮助我这样做的工具?

模式:

^\[(([\p{N}]*\]\,\[[\p{N}]*)*|[\p{N}]*)\]$

工作价值:

[1234567],[89023432],[124534543],[4564362],[1234543],[12234567],[124567],[1234567],[1234567]

灾难性的回溯值 - 如果值中有任何错误(最后添加额外的括号):

[1234567],[89023432],[124534543],[4564362],[1234543],[12234567],[124567],[1234567],[1234567]]

3 个答案:

答案 0 :(得分:5)

*是您的意思时,切勿使用+。我注意到你的正则表达式的第一件事是几乎所有东西都是可选的。只需要打开和关闭方括号,我很确定您不想将[]视为有效输入。

失控回溯的最大原因之一是有两个或更多可以匹配相同事物的替代方案。这是你|[\p{N}]*部分所得到的。正则表达式引擎必须在它放弃之前尝试通过字符串的每个可想到的路径,因此所有这些\p{N}*构造都会在每组数字上进行无休止的翻斗。

但是没有必要尝试解决这些问题,因为整体结构是错误的。我认为这就是你要找的东西:

^\[\p{N}+\](?:,\[\p{N}+\])*$

在使用第一个令牌([1234567])之后,如果字符串中的下一个内容不是逗号或字符串的结尾,则会立即失败。如果 看到逗号,则必须继续匹配另一个完整令牌([89023432]),否则会立即失败。

当您创建正则表达式时,这可能是最重要的事情:如果它会失败,您希望它尽快失败。您可以使用原子组和占有量词等功能,但如果正确使用正则表达式的结构,则很少需要它们。回溯并非不可避免。

答案 1 :(得分:1)

你的回溯循环是由字符串末尾的双]引起的 我在运行正则表达式之前清理字符串,将所有[[替换为[,将所有]]替换为]

答案 2 :(得分:0)

您可以使用原子分组或占有量词来避免这种灾难性的回溯。下面是一个使用原子分组的示例,在使用“坏”示例失败之前,只需要60步就可以计算:

^(?>\[((\p{N}*\]\,\[\p{N}*)*|\p{N}*)\])$

demo here