匹配正则表达式时程序永远运行

时间:2014-08-11 04:25:30

标签: java regex

我不知道为什么,但是当我尝试运行这个程序时,看起来该程序将永远运行。

package fjr.test;

import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class Test3 {

    public static void main(String[] args){

        String regex = "dssdfsdfdsf wdasdads dadlkn mdsds .";

        Pattern p  = Pattern.compile("^([a-zA-Z]+ *)+$"); 

        Matcher match =  p.matcher(regex); 

        if(match.matches()){
            System.out.println("Yess"); 
        }else{
            System.out.println("No.. "); 
        }

        System.out.println("FINISH..."); 
    }

}

我需要做的是匹配包含一串只用空格

分隔的单词的模式

4 个答案:

答案 0 :(得分:5)

您的计划可能遇到了所谓的灾难性回溯

如果你有一点时间,让我们来看看你的正则表达式是如何工作的......

快速复习:正则表达式如何工作:状态机始终从左向右读取,必要时回溯。

在左侧,我们有我们的模式:

/^([a-zA-Z]+ *)+$/

这是匹配的字符串:

dssdfsdfdsf wdasdads dadlkn mdsds .

从regex101调试器中,你的正则表达式需要78540步才能失败。这是因为您使用了贪婪不占有(回溯)的量词。

x

...长话短说,因为输入字符串无法匹配,正则表达式中的每个量词都会导致无限回溯 - 每个字符都从{{1}释放然后+,然后两个,然后从*释放一个组,以便更多地回溯。

以下是您应遵循的一些解决方案:

避免使用丰富的量词!

如果你修改你的表达式,你会发现该模式在逻辑上与:

相同
()+

这使用逻辑归纳的步骤来减少楼上的正则表达式以更快地匹配,现在是97步!

y

尽可能使用占有量词!

正如我所提到的,/^[a-zA-Z]+( +[a-zA-Z]+)*$/ 是邪恶的,因为它以强烈的方式回溯。我们是Java,我们能做什么?

此解决方案仅适用于/^([a-zA-Z]+ *)+$/[a-zA-Z]匹配不同的项目。我们可以使用占有团体!

这些简单的“/^([a-zA-Z]++ *+)++$/ ^ ^ ^ ”表示“如果我们从这里失败,我们就不会回溯”。这是一个非常有效的解决方案,并且不再需要回溯。每当你有两个不同的组,其间有量词时,请使用它们。如果您需要一些有效性的证据,这是我们的记分卡:

z

另请阅读:

在线演示:

答案 1 :(得分:2)

这会终止,但确实需要10秒左右。一些观察:

  • 从测试字符串的末尾删除fullstop会使其变快。
  • 将正则表达式中的*更改为+(我相信其实就是你想要的)会让它变快。我认为在该位置选择0个字符可以扩展状态空间。 我会用:

    ^(\ w + +)* \ w + $"

这意味着一堆(字空间),后跟一个字。反对你的例子,它很快

答案 2 :(得分:1)

您可以使用此正则表达式将所有单词与空格匹配或不匹配。

<强>样品:

 Pattern p  = Pattern.compile("([a-zA-Z ]+)"); 

答案 3 :(得分:0)

有趣的现象。这与贪心量词的行为有关。性能。
根据您的模式^([a-zA-Z]+ *)+$和您的输入"dssdfsdfdsf wdasdads dadlkn mdsds .",该输出不符合您的输入,但是,java正则表达式将回溯^([a-zA-Z]+ *)+的所有可能性,(见下文)例子)然后获得不匹配的结果。

(dssdfsdfdsf )(wdasdads )(dadlkn )(mdsds )
(dssdfsdfdsf )(wdasdads )(dadlkn )(mdsd)(s )
(dssdfsdfdsf )(wdasdads )(dadlkn )(mds)(ds )
...
(d)(s)(s)(d)(f)(s)(d)(f)(d)(s)(f )(w)(d)(a)(s)(d)(a)(d)(s )(d)(a)(d)(l)(k)(n )(m)(d)(s)(d)(s )
...
(dssdfsdfdsf )
...
(d)

回溯可能超过200,000,000次 我有点好奇为什么java-regex可以做一些性能改进,因为在第一次尝试之后,它发现了epxected char是&#39;。&#39;而不是&#39; $& #39;然后任何进一步的回溯都不会成功。做回溯是没用的。

因此,当我们定义循环模式时,我们需要更多地注意内部循环模式(例如:在您的示例中为[a-zA-Z]+ *),而不是使它与许多情况匹配。否则,整个Loop的回溯数将是巨大的 另一个类似的例子(比你的情况更糟糕):
模式:&#34;(。+)* A&#34;
输入:&#34; abcdefghijk lmnopqrs tuvwxy zzzzz A&#34; - 这么快 输入:&#34; abcdefghijAk lmnopqrs tuvwxy zzzzz&#34; - 不错,等一会儿 输入:&#34; aAbcdefghijk lmnopqrs tuvwxy zzzzz&#34; - 似乎无限。 (实际上,不是,但我没有耐心等待它的完成。)