Java模式匹配进入无限循环

时间:2011-10-26 04:58:29

标签: java regex

一位朋友给了我这段代码并说有一个错误。是的,这段代码永远运行。

我得到的答案是:

  

在打印任何内容之前,它会运行> 10 ^ 15年。

public class Match {
     public static void main(String[] args) {
         Pattern p = Pattern.compile("(aa|aab?)+");
         int count = 0;
         for(String s = ""; s.length() < 200; s += "a")
             if (p.matcher(s).matches())
                 count++;
         System.out.println(count);
     }
}

我真的不明白为什么我会看到这种行为,我是java新手,您有什么建议吗?

3 个答案:

答案 0 :(得分:19)

根据OWASP,你正在使用的模式被称为邪恶的正则表达式(他们知道他们在大多数时间都在讨论什么):

https://www.owasp.org/index.php/Regular_expression_Denial_of_Service_-_ReDoS

它基本上与aaaaaab匹配(因为通过添加? b是可选的)

像这样的正则表达式容易受到ReDoS或Regex拒绝服务攻击。

所以是的,理清你想要匹配的东西。我建议在上面的示例中,您只需匹配aa,不需要群组,重复或替换:

Pattern p = Pattern.compile("aa");

同样有人指出,谁现在删除了他的帖子,你不应该使用+ =附加到字符串。您应该使用StringBuffer代替:

public class Match {
  public static void main(String[] args) {
    Pattern p = Pattern.compile("aa");
    StringBuffer buffy = new StringBuffer(200);
    int count = 0;
    for(int i = 0; i < 200; i++){
      buffy.append("a")
      if (p.matcher(buffy.toString()).matches()){
        count++;
      }
    }
    System.out.println(count);
  }
}

答案 1 :(得分:12)

这个节目来自 Josh Bloch和Bill Pugh @ Devoxx'10 的Java puzzler演示,观看它here。我认为他们的解释是最好的。

它在幻灯片31中。但是不要跳过任何充满乐趣的幻灯片。

答案 2 :(得分:1)

正则表达式(aa|aab?)+是一个需要特别长时间才能使正则表达式引擎处理的表达式。这些被称为evil regexes。它类似于链接上的(a|aa)+示例。对于完全由a s组成的字符串,这个特殊的字符非常慢。

这段代码的作用是检查邪恶的正则表达式对越来越长的a s字符串,最长为200,所以它肯定需要很长时间,并且它不会打印直到循环结束。我有兴趣知道10 ^ 15年的数字来自哪里。

修改

好的,10 ^ 15(实际上是问题中的整段代码)来自this talk,幻灯片37.感谢zengr的链接。对该问题最相关的信息是对该正则表达式的检查需要时间,该时间是字符串长度的指数。具体来说它是O(2 ^(n / 2)),因此检查最后一个字符串比第一个字符串花费2 ^ 99(左右)。