如果你在工作经常使用正则表达式的话,你可能对灾难性回溯(catastrophic backtracking)这个概念并不陌生,这说明你在强迫正则引擎去进行指数级的排列运算。比如说,
点击这里运行下这个用例来看看它得花多长时间(大概是5到10秒):
public class LongRunningRegexExample
{
public static void main(String[] args) throws InterruptedException
{
final Pattern pattern = Pattern.compile("(0*)*A");
final String input = "00000000000000000000000000";
long startTime = System.currentTimeMillis();
Matcher matcher = pattern.matcher(input);
matcher.find(); // runs for a long time!
System.out.println("Regex took:" + (System.currentTimeMillis() - startTime) + "ms");
}
}
然而只需一个很小的改动就能让它马上返回。为什么?引用 Dzone的Andreas Haufler在他的_
如何使用正则表达式杀掉Java程序_一文中的一句话,这个简洁的示例程序也是来自这篇文章的:
乍一看它像是一个无限循环,这其实是一个灾难性回溯。这说明匹配器在输入结尾处没有找到'A'字符。因此外部检测器会后退一步,而内部的则前进一步,但仍然没有发现。
因此匹配器会不断地回退,尝试完所有的组合,来看一下是否存在一个匹配的字符串。它最终仍会返回(没有匹配上),但时间复杂度却是指数级的(增加一个字符运行时间会翻倍)。
那么我们该如何做才能避免这样的影响,有没有可能会遇到这么一个场景,要让Java进程运行好几天,甚至好几年的?
如果你不嫌麻烦的话,你可以在自己的正则模式中注意去避免这样的情况。但是如果你部署了一个应用,它接受一个输入作为正则表达式的话——比如说一个在线的Java正则校验器,那么你需要防止这种情况,否则就可能会遭受拒绝服务攻击。
答案非常简单,不过你需要引入一个线程,下面是我在stackoverflow上找到的一个特殊的字符序列的实现。
public class InterruptibleRegexExample
{
public static void main(String[] args) throws InterruptedException
{
final Pattern pattern = Pattern.compile("(0*)*A");
final String input = "00000000000000000000000000";
Runnable runnable = new Runnable() {
@Override
public void run()
{
long startTime = System.currentTimeMillis();
Matcher interruptableMatcher = pattern.matcher(new InterruptibleCharSequence(input));
interruptableMatcher.find(); // runs for a long time!
System.out.println("Regex took:" + (System.currentTimeMillis() - startTime) + "ms");
}
};
Thread thread = new Thread(runnable);
thread.start();
Thread.sleep(500);
thread.interrupt();
}
}
这样这个正则就非常棒了,它是可中断的。
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,这多少会影响到程序的性能,不过总比干等一年要好得多:
/**
* {@link CharSequence} that notices {@link Thread} interrupts
*
* @author gojomo - http://stackoverflow.com/questions/910740/cancelling-a-long-running-regex-match
*/
private static class InterruptibleCharSequence implements CharSequence {
CharSequence inner;
public InterruptibleCharSequence(CharSequence inner) {
super();
this.inner = inner;
}
@Override
public char charAt(int index) {
if (Thread.currentThread().isInterrupted()) {
throw new RuntimeException("Interrupted!");
}
return inner.charAt(index);
}
@Override
public int length() {
return inner.length();
}
@Override
public CharSequence subSequence(int start, int end) {
return new InterruptibleCharSequence(inner.subSequence(start, end));
}
@Override
public String toString() {
return inner.toString();
}
}
原创文章转载请注明出处:
http://it.deepinmind.com
英文原文链接
分享到:
相关推荐
java超时取消正则表达式匹配方法,代码超时处理,设置代码执行时间,超棒的工具类 lambda,Callable,ExecutorService,超过执行5秒退出
标题提到的"超时方法 正则超时"是关于如何在C#中处理正则表达式执行时间过长的问题。下面将详细介绍这一主题。 首先,`OutTimeClass.cs`可能包含了一个自定义的超时类,它可能提供了设置超时时间、执行任务以及在超...
JavaScript的RegExp对象和String对象定义了使用正则表达式来执行强大的模式匹配和文本检索与替换函数的方法. 在JavaScript中,正则表达式是由一个RegExp对象表示的.当然,可以使用一个RegExp()构造函数来创建RegExp...
根据给定文件的信息,我们可以从中提炼出一系列与Visual Basic for Applications (VBA)相关的知识点,以及部分关于C#字符串和正则表达式的概念。以下是对这些知识点的详细解析: ### VBA语句集 #### 1. Option ...
其中,`<regex>`是匹配的正则表达式,`<replacement>`是替换后的URL,`[flag]`是一些可选的标志,如`last`、`break`、`redirect`、`permanent`等,它们控制着重写规则的执行行为。 二、Rewrite执行顺序 Nginx中的...
页面上还包含了PHP脚本执行的入口,通过设置`set_time_limit(0)`可以确保长时间运行的脚本不会因时间限制而中断。 总结来说,通过使用PHP正则表达式匹配远程图片地址,并利用文件处理函数将图片下载并保存到本地...
这个工具在不中断服务的情况下能够创建数据的物理备份,极大地提高了备份效率。在使用`mysqlhotcopy`时,我们可以结合正则表达式来更精确地选择需要备份的数据。 首先,确保已经为`mysqlhotcopy`分配了一个具有适当...
2. **代码与数据分配**:`vectors.o (+RO, +FIRST)`确保中断向量表被放置在最前面的位置。`*(InRoot$$Sections)`复制必需处于rootregion的库段。`*(+RW, +ZI)`将可写数据段和未初始化数据段放入SRAM中,而`stack.o ...
taskENTER_CRITICAL和taskEXIT_CRITICAL宏可以用来创建临界区,以防止中断打断代码片段的执行。taskYIELD函数则用于主动放弃CPU的使用权,允许其他就绪态任务运行。 5. 列表(Lists)模块是FreeRTOS中用来管理数据...
错误处理通常包括产生错误消息和中断程序执行。 9. **优化** 高级解释器可能包含一些优化策略,如死代码删除、常量折叠等,以提高代码执行效率。 10. **跨平台能力** Basic解释器的源代码通常设计为可移植的,...
另一个常用的工具是trap命令,它能够捕获脚本运行期间的信号,如SIGINT(中断信号),并执行预定义的动作。此外,还可以利用脚本内部的echo命令输出变量和执行流的信息来帮助我们理解脚本的执行过程。 Shell脚本的...
在编程中,异常是程序执行期间发生的不正常条件,它中断了正常的代码流程。异常处理是编程语言中的一个重要概念,用于捕获并处理这些错误,以确保程序的稳定性和可靠性。 描述中提供的链接指向了一篇关于技术博客的...
精易编程助手通过优化调试界面,为用户提供了一个直观的匹配过程展示,用户能够一步步地观察到正则表达式的执行情况,从而轻松定位问题所在。这种人性化的交互设计,不仅让正则调试变得简单易行,也大大提升了用户的...
管理流程允许用户控制脚本的执行流程,如中断、跳过或退出循环。脚本调度执行是自动化管理的重要部分,PowerShell提供了相关的cmdlet来设置和管理定时任务。可扩展性和代码重用是编程中的重要概念,PowerShell支持...
异常(Exception)是程序执行期间发生的错误,它中断了正常的控制流程。Java提供了异常处理机制来优雅地处理这些异常,而不是让程序突然崩溃。 在Java中,异常捕获通过`try-catch`语句块实现。`try`块包含可能会抛...
- **`DisconnectObject`**:从当前脚本中断开与某个对象的连接。 - **`Echo`**:用于输出信息到控制台。 - **`GetObject`**:获取已存在的COM对象。 - **`Quit`**:终止脚本运行。 - **`Sleep`**:使脚本暂停执行...
这时,我们需要使用try-catch块来捕获并处理这些异常,防止程序因意外情况而中断。 6. **并发与线程安全**: 如果系统需要支持多个用户同时访问,那么需要考虑并发控制。Java的synchronized关键字和Thread类可以...
此外,还有**Timer**选项,可在setTimeout或setInterval处理函数开始执行时中断。 ##### 5. JavaScript异常中断 - **Pretty Print**按钮左侧有一个开关,用于控制JavaScript抛出异常时是否中断。可以选择在所有...
异常处理则涉及到如何使用try、catch、finally关键字来捕获和处理程序执行过程中可能发生的异常情况,以避免程序因错误而中断执行。 接口和包是Java语言中用于组织代码的高级概念。接口定义了一组方法规范,供不同...