与大写字母相关的Javascript正则表达式无限循环

标签 javascript regex node.js v8

我在 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/

相关文章:

node.js - "npm init"不创建node_module文件夹及其他文件

javascript - 如何使用jsPDF设置图像以适合页面宽度?

javascript - promise 返回前不解决

javascript - ASP.NET 中通用容器的设计模式

regex - 仅替换文本文件特定行中的数字

javascript - node.js 可以用作在 Web 应用程序中运行任意服务器端 Javascript 的框架吗?

javascript - 点击事件时页面会自动重新加载。无法确定原因?

arrays - MongoDB 查询 $in 与元素的正则表达式数组

python - 使用正前瞻或后顾的增强赋值操作的正则表达式

javascript - 如何使用 execa 执行一系列命令?