javascript - 为什么这个 JavaScript 正则表达式会在浏览器中崩溃?

标签 javascript regex browser

据我所知,[ab](a|b) 在尝试匹配一组字符时在目的上应该是等效的。现在,看看两个正则表达式:

/^(\s|\u00A0)+|(\s|\u00A0)+$/g
/^[\s\u00A0]+|[\s\u00A0]+$/g

它们都应该匹配字符串开头和结尾的空格(有关正则表达式本身的更多信息,请参阅 Polyfill here 部分)。使用方括号时一切正常,但是当您切换到括号时,即使是最简单的字符串也会导致浏览器似乎无限期地运行。这发生在最新的 Chrome 和 Firefox 上。

This jsfiddle证明了这一点:

a ="a                                                               b";

// Doesn't work
// alert(a.replace(/^(\s|\u00A0)+|(\s|\u00A0)+$/g,''));
// Works
alert(a.replace(/^[\s\u00A0]+|[\s\u00A0]+$/g,''));

这是浏览器正则表达式引擎实现的疯狂怪癖,还是正则表达式算法有其他原因导致的?

最佳答案

如解释的那样,您看到的问题称为灾难性回溯 here .

首先,让我简化并阐明您的测试用例:

a = Array(30).join("\u00a0") + "b";  // A string with 30 consecutive \u00a0
s = Date.now();
t = a.replace(/^(\s|\u00A0)+$/g, '');
console.log(Date.now()-s, a.length);

表达式的第二部分发生了什么:^(\s|\u00A0)+$。请注意,\s 匹配许多空白字符,包括 \u00A0 本身。这意味着 \s\u00A0 都匹配 30 个 \u00A0 字符中的每一个。

因此,如果您尝试将字符串与 /(\s|\u00A0)+/ 进行匹配,您会发现每个 2^30 的不同组合30 个字符的空白模式将导致匹配。当正则表达式匹配器匹配前 30 个字符时,它将尝试匹配字符串结尾 ($) 但失败了,因此它回溯并最终尝试所有 2^30 组合.

您的原始字符串(在 jsfiddle 中,stackflow 中的字符串已经“标准化”为所有空格)是 a\u00a0\u00a0 ...\u00a0 b 大约有 30 个 \u00a0 个字符,因此浏览器大约需要 2^30 的努力才能完成。它不会挂起浏览器,但需要几分钟才能完成。

关于javascript - 为什么这个 JavaScript 正则表达式会在浏览器中崩溃?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/30760557/

相关文章:

javascript - 检测浏览器功能和鼠标和触摸的选择性事件

html - 如何在不使用 WebBrowser 控件的情况下打印网页

javascript - 加载缓慢的div死空间

javascript - Laravel - 在保存到数据库之前删除 JSON header

javascript - 使用 Ajax 获取值并发送 url

javascript - 检测浏览器是否使用隐私浏览模式

python - 在正则表达式中搜索到字符串末尾

php - 正则表达式:在不在文本区域中的字符串中查找换行符

javascript - 正则表达式为首字母并且它包含一个词

DIV 元素中的 HTML 表格随着浏览器调整大小而不断移动