正则表达式测试器 打开工具

灾难性回溯:为什么一条正则能让浏览器卡死

正则引擎在匹配失败时会退回上一个位置换个走法再试——这叫回溯。嵌套量词(如 (a+)+)让「换个走法」的组合数随文本长度指数增长,于是在一段并不长的文本上,引擎要尝试天文数字种组合。

一个具体例子

正则 ^(a+)+$,测试文本 aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa!(30 个 a 加一个感叹号)。

引擎会尝试把 30 个 a 分成 1 组、2 组、3 组……每一种分法都试一遍,全部失败后才发现末尾的感叹号不匹配。组合数是 229 量级,约 5 亿次尝试。

再长 10 个字符,就是 5000 亿次。这就是「多一个字符就卡死」的原因——增长是指数的,不是线性的。

怎么识别危险写法

三个信号:

经验判断:看到「括号里带量词,括号外又带量词」,就该警惕。

三种改写方式

  1. 去掉嵌套:(a+)+ 等价于 a+。绝大多数嵌套量词都是多余的,先想清楚它到底想表达什么。
  2. 用原子组或占有量词:JavaScript 不支持原子组,但可以用前瞻模拟;部分引擎(PCRE)支持 (?>...) 与 a++。
  3. 改用更精确的字符类:把 (\w+)* 换成 [\w ]*(明确允许哪些字符),组合数立刻回到线性。

最有效的是第一条:先问「这个嵌套量词真的必要吗」,九成情况下答案是「不必要」。

这个工具怎么保护你

正则在 Web Worker 里执行。Worker 是独立线程,卡住它不影响主线程——页面照样能滚动、能输入。

一旦超过 1.5 秒没有结果,页面立即终止该 Worker 并给出提示。你的输入与页面都完好无损,超时的 Worker 被丢弃,下一次输入会新建一个干净的 Worker 重新执行,不需要刷新页面。

这段逻辑的价值在于:它把「浏览器卡死到只能强制关闭」变成「等一秒半,看到一句提示」。调正则时这个差别很大。

上面这些都不用装软件,也不用注册:打开页面就能用。

打开正则表达式测试器 →

常见问题

超时后我还能继续用吗?

能。超时的 Worker 被立刻终止并丢弃,下一次输入会新建一个干净的 Worker 重新执行,不需要刷新页面。

为什么我的正则在小文本上很快,大文本就卡死?

灾难性回溯的耗时随文本长度指数增长。20 个字符可能几毫秒,30 个字符就要几秒。所以「小样本测试通过」不代表安全。

所有正则引擎都有这个问题吗?

回溯型引擎(JavaScript、Python re、PCRE、Java)都有。RE2、Rust regex 这类基于自动机的引擎没有回溯,最坏情况是线性时间,但不支持反向引用与部分高级语法。