一位朋友给了我这段代码并说有一个错误。是的,这段代码永远运行。
我得到的答案是:
在打印任何内容之前,它会运行> 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新手,您有什么建议吗?
答案 0 :(得分:19)
根据OWASP,你正在使用的模式被称为邪恶的正则表达式(他们知道他们在大多数时间都在讨论什么):
https://www.owasp.org/index.php/Regular_expression_Denial_of_Service_-_ReDoS
它基本上与aa
或aa
或aab
匹配(因为通过添加?
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(左右)。