正则表达式挂起程序(100% CPU 使用率)

2022-09-01 13:38:56

Java在100%CPU使用率的情况下挂起,当我使用下面的字符串作为正则表达式的输入时。

使用的正则表达式:

下面是用于我的应用程序中的描述字段的正则表达式。

^([A-Za-z0-9\\-\\_\\.\\&\\,]+[\\s]*)+

用于测试的字符串:

SaaS服务VLAN从Provider_One
第二次尝试与Didier SPT,因为他给我的第一个是错误的:-(

当我将同一字符串拆分为不同的组合时,它可以正常工作。比如“来自Provider_One的SaaS Service VLAN”,“他给我的第一个是错误的:-(”等。Java仅针对上述给定字符串挂起。

我还尝试优化正则表达式,如下所示。

^([\\w\\-\\.\\&\\,]+[\\s]*)+

即使这样也不起作用。


答案 1

另一个灾难性回溯的经典案例。

您有嵌套的量词,当正则表达式到达不属于字符类的输入字符串时,会导致检查大量排列(假设您正在使用该方法)。:.matches()

让我们将问题简化为这个正则表达式:

^([^:]+)+$

和此字符串:

1234:

正则表达式引擎需要检查

1234    # no repetition of the capturing group
123 4   # first repetition of the group: 123; second repetition: 4
12 34   # etc.
12 3 4 
1 234
1 23 4
1 2 34
1 2 3 4

...而这还只是四个角色。在示例字符串上,RegexBuddy 在尝试 100 万次后中止。Java会很高兴地继续摇摆不定...在最终承认这些组合都不允许以下匹配之前。:

你怎么能解决这个问题?

您可以使用所有格量词禁止正则表达式回溯:

^([A-Za-z0-9_.&,-]++\\s*+)+

将允许正则表达式更快地失败。顺便说一句,我删除了所有这些不必要的反斜杠。

编辑:

一些测量:

在字符串上,需要正则表达式Buddy 862步骤才能找出不匹配项。
对于 ,它是 1,742 步。
对于 ,14,014 步。
对于 ,28,046 步。
对于 ,112,222 步。
对于 ,>1,000,000 步。"was wrong :-)""me was wrong :-)""gave me was wrong :-)""he gave me was wrong :-)""one he gave me was wrong :-)""first one he gave me was wrong :-)"


答案 2

首先,您需要意识到您的正则表达式无法与提供的输入字符串匹配。字符串包含许多不是“单词”字符的字符( 和 )。'<' '>' '/' ':'')'

那么,为什么需要这么长时间呢?

基本上是“灾难性的回溯”。更具体地说,正则表达式的重复结构为正则表达式回溯算法提供了指数级数量的替代方案!

这是您的正则表达式所说的:

  1. 一个或多个单词字符
  2. 后跟零个或多个空格字符
  3. 根据需要重复前 2 个模式。

问题在于“零个或多个空格字符”部分。第一次,匹配器会将所有内容与第一个意外字符(即 )进行匹配。然后它会退后一点,然后用不同的替代方案再次尝试...在最后一个字母之前涉及“零空格”,然后当它失败时,它将把“零空格”向后移动一个位置。'<'

问题在于,对于具有非空格字符的 String,可以匹配“零空格”的不同位置,并且会产生不同的组合。随着增长,这迅速变成一个巨大的数字,最终结果很难与无限循环区分开来。NN2^NN