我在 node.js 中创建了一个正则表达式,能够匹配看起来像 URL 的文本部分。但是,在某些文本上,我的正则表达式创建了一个无限循环!
整个正则表达式是:
/(((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
我已经做了更多测试来了解这个问题。首先,没有标志:
> r = /((ftp|https?):\/\/|(www\.))?([a-z\d]+-?)+\./
> r.test(txt)
true
令人惊讶的是,它有效!
仅使用 g
标志测试 2:
> r = /((ftp|https?):\/\/|(www\.))?([a-z\d]+-?)+\./g
> r.test(txt)
true
也可以!
仅使用 i
标志测试 3:
> r = /((ftp|https?):\/\/|(www\.))?([a-z\d]+-?)+\./i
> r.test(txt)
^CError: Script execution interrupted. // infinite loop
失败...所以我决定删除 i
标志并改用 A-Z:
> r = /((ftp|https?):\/\/|(www\.))?([a-zA-Z\d]+-?)+\./
> r.test(txt)
^CError: Script execution interrupted. // infinite loop
不工作...有人理解这个问题吗?有解决办法吗?
最佳答案
这是灾难性的回溯。
为避免这种现象,请尝试构建一个始终从左到右进行的正则表达式(即匹配失败将停止正则表达式,而不是让它在其他地方尝试某些子部分)。换句话说:不应该有多种方法来匹配您的字符串的一部分(否则正则表达式引擎将尝试所有这些,在某些情况下,这可能会导致成倍增长的可能性树)。
为了了解您的真正目标,我可以提出这个解决方案:
/((ftp|https?):\/\/|(www\.))?([a-z\d]+)(-[a-z\d]+)*\./gi
(这也可能更正确)
另一种解决方案是使用非回溯正则表达式引擎,例如 R2 (是的,有一个 Node 绑定(bind))。根据我的经验, Node 中的 R2 比标准正则表达式引擎慢,因此只有当您有用户输入正则表达式时它才有意义,因为当您是正则表达式作者时,您通常可以找到一个非灾难性的正则表达式。
关于与大写字母相关的Javascript正则表达式无限循环,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/41721227/