我在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
不工作......有人理解这个问题吗?有解决方案吗?
答案 0 :(得分:1)
这是一次灾难性的回溯。
为了避免这种现象,尝试构建一个总是从左到右进行的正则表达式(即匹配失败会阻止正则表达式,而不是让它尝试其他地方的某个子部分)。换句话说:不应该有几种方法匹配你的字符串的一部分(或者正则表达式引擎会尝试所有这些,在某些情况下,这可能会导致指数大小的可能性树)。
试图了解你的真正目标,我可以提出这个解决方案:
/((ftp|https?):\/\/|(www\.))?([a-z\d]+)(-[a-z\d]+)*\./gi
(这也可能更正确)
另一个解决方案是使用非回溯正则表达式引擎,例如R2(是的,节点有绑定)。根据我的经验,节点中的R2比标准的正则表达式引擎慢,所以只有当你有用户输入的正则表达式才有意义,因为当你是正则表达式作者时,你最常见的就是找到一个非灾难性的正则表达式。