Javascript regexp与大写字母相关的无限循环

时间:2017-01-18 13:51:47

标签: javascript regex node.js v8

我在node.js中创建了一个regexp,它能够匹配看起来像URL的部分文本。但是,在某些文本中,我的正则表达式创建了一个无限循环!

整个正则表达式是:

public static void main(String[] args) throws Exception {

    Configurations configurations = new Configurations();
    CompositeConfiguration compositeConfiguration = new CompositeConfiguration();

    Enumeration<URL> urls = ConfigurationTest2.class.getClassLoader().getResources("test.properties");
    while(urls.hasMoreElements()) {
        PropertiesConfiguration propertiesConfiguration = configurations.properties(urls.nextElement());
        compositeConfiguration.addConfiguration(propertiesConfiguration);
    }
    System.out.println(compositeConfiguration.getString("test1"));

}

但经过一些调试后,我发现无限循环只由这部分引起:/(((ftp|https?):\/\/|(www\.))?([a-z\d]+-?)+\.[a-z\d/\-._~:?#@!$&'*+,;=`]+)/gi

/((ftp|https?):\/\/|(www\.))?([a-z\d]+-?)+\./gi

我做了更多的测试来理解这个问题。首先,没有旗帜:

> let r = /((ftp|https?):\/\/|(www\.))?([a-z\d]+-?)+\./gi
> const txt = "www.ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_~:/?#@!$&'*+,;=`"
> r.test(txt)
^CError: Script execution interrupted. // infinite loop

令人惊讶的是,它有效! 测试2只有> r = /((ftp|https?):\/\/|(www\.))?([a-z\d]+-?)+\./ > r.test(txt) true 标志:

g

也可以! 测试3只有> r = /((ftp|https?):\/\/|(www\.))?([a-z\d]+-?)+\./g > r.test(txt) true 标志:

i

失败......所以我决定删除> r = /((ftp|https?):\/\/|(www\.))?([a-z\d]+-?)+\./i > r.test(txt) ^CError: Script execution interrupted. // infinite loop 标志并改为使用A-Z:

i

不工作......有人理解这个问题吗?有解决方案吗?

1 个答案:

答案 0 :(得分:1)

这是一次灾难性的回溯。

为了避免这种现象,尝试构建一个总是从左到右进行的正则表达式(即匹配失败会阻止正则表达式,而不是让它尝试其他地方的某个子部分)。换句话说:不应该有几种方法匹配你的字符串的一部分(或者正则表达式引擎会尝试所有这些,在某些情况下,这可能会导致指数大小的可能性树)。

试图了解你的真正目标,我可以提出这个解决方案:

/((ftp|https?):\/\/|(www\.))?([a-z\d]+)(-[a-z\d]+)*\./gi

(这也可能更正确)

另一个解决方案是使用非回溯正则表达式引擎,例如R2(是的,节点有绑定)。根据我的经验,节点中的R2比标准的正则表达式引擎慢,所以只有当你有用户输入的正则表达式才有意义,因为当你是正则表达式作者时,你最常见的就是找到一个非灾难性的正则表达式。