如何中断一个长时间运行的”无限”Java正则表达式

来源:互联网 发布:网络商标注册费用 编辑:程序博客网 时间:2024/04/28 15:35


本文由 ImportNew - 人晓 翻译自 ocpsoft。欢迎加入Java小组。转载请参见文章末尾的要求。

如果你处理过大量的正则表达式,那么你对“catastrophic backtracking”的概念一定不陌生,这种情况下处理器被强迫执行指数倍的计算。例如,点击该示例,看看它需要多久完成(应该在5-10秒后超时)。
LongRunningRegexExample.java

view source
01public class LongRunningRegexExample
02{
03  publicstatic void main(String[] args) throws InterruptedException
04  {
05  finalPattern pattern = Pattern.compile("(0*)*A");
06  finalString input = "00000000000000000000000000";
07 
08  longstartTime = System.currentTimeMillis();
09  Matcher matcher = pattern.matcher(input);
10  matcher.find();// runs for a long time!
11  System.out.println("Regex took:"+ (System.currentTimeMillis() - startTime) + "ms");
12  }
13}

但是,小小的改动就能让该程序立即响应。为什么呢?这里引用Dzone中Andreas Haufler的文章《How to Kill Java with a Regular Expression》,其中提到了这个非常简洁的例子:

  • 第一眼看上去像是无限循环的东西会造成catastrophic backtracking
  • 意思是说匹配器在输入的末尾并没有检测到”A”。现在外侧的限定符后退一次,内存的则前进一次,如此重复,无法得到结果。
  • 因此,匹配器逐步回退,并尝试所有的组合以找出匹配符号。它最终将返回(没有匹配的结果),但是该过程的复杂性是指数型的(输入中添加一个字符加倍了运行时间)。

所以,我们究竟该采取何种方式避免这种破坏性的影响,我们是否会遇到Java程序运行数天甚至几年的情形?

在你的资源非常充足的情况下,你也许可以简单小心的在你的模式中避免这种情况。但是,如果你正在开发一个接受正则表达式作为输入的应用程序,例如一个在线Java可视化的正则表达式示例,那么你就应该避免这种情情况或是受到拒绝服务器攻击。

幸运的是,答案非常简单,但需要引入一个线程,以及一个非常特殊的字符串序列。我是在StackOverflow上找到该实现的。
InterruptibleRegexExample.java

view source
01public class InterruptibleRegexExample
02{
03public static void main(String[] args)throws InterruptedException
04{
05final Pattern pattern = Pattern.compile("(0*)*A");
06final String input = "00000000000000000000000000";
07Runnable runnable = newRunnable() {
08@Override
09public void run()
10{
11long startTime = System.currentTimeMillis();
12Matcher interruptableMatcher = pattern.matcher(newInterruptibleCharSequence(input));
13interruptableMatcher.find(); // runs for a long time!
14System.out.println("Regex took:"+ (System.currentTimeMillis() - startTime) + "ms");
15}
16};
17 
18Thread thread = newThread(runnable);
19thread.start();
20 
21Thread.sleep(500);
22thread.interrupt();
23}
24}

 

结果很好的中断了正则表达式:

Exception in thread “Thread-2″ java.lang.RuntimeException: Die! … Why won’t you DIE!
at org.ocpsoft.tutorial.regex.server.InterruptRegex$InterruptibleCharSequence.charAt(InterruptRegex.java:72)
at java.util.regex.Pattern$BmpCharProperty.match(Pattern.java:3366)
at java.util.regex.Pattern$Curly.match0(Pattern.java:3760)
at java.util.regex.Pattern$Curly.match(Pattern.java:3744)
at java.util.regex.Pattern$GroupHead.match(Pattern.java:4168)

你还需要一个InterruptibleCharSequence类,它在某种程度上也会影响性能,但是要比等上一年要好的多:
InterruptibleCharSequence.java

view source
01/**
02* {@link CharSequence} that notices {@link Thread} interrupts
03*
04* @author gojomo
05*/
06private static class InterruptibleCharSequenceimplements CharSequence {
07CharSequence inner;
08public InterruptibleCharSequence(CharSequence inner) {
09super();
10this.inner = inner;
11}
12 
13@Override
14public char charAt(intindex) {
15if (Thread.currentThread().isInterrupted()) {
16throw new RuntimeException("Interrupted!");
17}
18return inner.charAt(index);
19}
20 
21@Override
22public int length() {
23return inner.length();
24}
25 
26@Override
27public CharSequence subSequence(int start, int end) {
28return new InterruptibleCharSequence(inner.subSequence(start, end));
29}
30 
31@Override
32public String toString() {
33return inner.toString();
34}
35}

 

结论

我很开心能学习并撰写这些内容,这篇博客总结了正则表达式的无限运行及解决方案(Java中),希望它能帮到其他遇到该问题的人。愉快的过程!
原文链接: ocpsoft 翻译: ImportNew.com - 人晓
译文链接: http://www.importnew.com/11975.html

0 0
原创粉丝点击