跳到正文
DevTools / 正则表达式测试

正则表达式测试

正则表达式如果不能看到它匹配了什么,就基本是“写时明白、读时糊涂”。本测试器在你输入时实时高亮每一处匹配,单独列出每个编号捕获组和命名捕获组,预览带反向引用的替换结果,并对那些不报错但会给出错误答案的写法给出提示——未转义的点号、本该懒惰却写成贪婪的量词、以及只在一端加了锚点的模式。

完全在浏览器本地运行——不会上传任何内容。

//gi

图库

测试文本
匹配结果: 2
ada@example.com, not-an-email, grace@dev.co.uk

捕获组

#1 @ 0ada@example.com
#2 @ 31grace@dev.co.uk

工作原理

匹配由 JavaScript 自身的正则引擎驱动,也就是会在浏览器或 Node 中运行你这段模式的同一个引擎,所见即所得。这很重要,因为各引擎的差异很容易被忽略:后行断言在 JavaScript 和 PCRE 中可用,在 Go 的 RE2 中不可用;占有量词和原子组是 PCRE 独有的。如果生产代码运行在其他引擎上,在这里测试只能验证模式的逻辑,不能验证它的可移植性。

标志位对模式含义的影响超出多数人的预期。g 标志让引擎查找全部匹配而不是在首个匹配处停下,它的缺失正是 replace 经常只改掉一处的原因。i 让匹配忽略大小写;m 让 ^ 和 $ 匹配行边界而不是整串的首尾;s 让点号匹配换行;u 启用完整 Unicode 模式,此时字符类理解的是码点而不是 UTF-16 码元——这正是能否正确匹配一个 emoji 与只匹配到它一半的区别。

捕获组按左括号出现的顺序编号,嵌套组按“在哪里开始”而非“在哪里结束”编号。被量词修饰的组只捕获最后一次迭代的结果,这正是看起来应该捕获一串内容的模式却只返回一项的经典原因。使用 (?<name>...) 语法的命名组彻底绕开了计数问题,本工具会按名称连同位置一并列出,使模式与提取值之间的关系一目了然,而不必靠推断。

有两种失败模式值得明确警告。含有嵌套量词的模式,例如 (a+)+b,在遇到不匹配的输入时可能耗时呈指数增长,这就是灾难性回溯,当模式作用于用户输入时是真实的拒绝服务风险——30 个字符的字符串就足以让进程卡死。另一种是未加锚点的模式:\d{4} 会愉快地匹配更长数字的前四位,而加上 ^ 和 $ 往往才是本意。两者都会被报告出来,而不是留给你自己去踩。

常见应用场景

  • 编写并调试邮箱、URL、电话号码或语义化版本号的校验模式。
  • 针对真实日志样本编写提取字段的正则,而不是凭空猜测。
  • 在编写读取代码之前,确认某个值落在了哪个捕获组里。
  • 预览带反向引用的替换效果,确认输出格式后再应用到文件上。
  • 在上线到服务器之前,检查模式是否存在灾难性回溯。
  • 把难以阅读的模式转成自然语言解释,用于代码评审或注释。

最后更新: 2026-10-14

常见问题解答

为什么替换只改了第一处?

因为缺少全局标志。没有 g 时正则表达式在首个匹配处停止,String.replace 和 String.matchAll 的行为都随之而定。加上标志后引擎会从每个匹配的结束位置继续。相关的另一个意外是:如果模式能匹配空字符串,重复匹配可能陷入死循环,所以“能匹配空串”的全局模式需要显式的保护条件。

什么是灾难性回溯?

当回溯引擎面对一个最终匹配失败的输入时,需要尝试指数级数量的匹配路径,这就是灾难性回溯。含嵌套量词的模式——(a+)+、(a|a)*、(.*)*——是常见成因,每多一个字符运行时间就翻倍,因此 30 个字符的输入就可能耗时数分钟。防御手段是避免嵌套量词、在引擎支持时改为占有量词或原子组,或者改用 RE2 这类完全没有回溯的线性时间引擎。

这里用的是哪种正则方言?

ECMAScript 定义的 JavaScript 方言。它支持先行断言、后行断言、命名组、配合 u 标志的 Unicode 属性转义,以及配合 y 标志的粘性匹配。它不支持占有量词、原子组、递归和条件匹配,这些都是 PCRE 的特性。如果后端使用 PCRE、Java 或 .NET,日常模式的行为基本一致,但凡是用到这些高级构造的地方,都必须到目标引擎里验证。

如何跨换行匹配?

这涉及两件独立的事。除非设置 s 标志,点号不匹配行终止符,所以 a.*b 无法跨行,而 a[\s\S]*b 则可以。另外,除非设置 m 标志,^ 和 $ 只在整串的首尾匹配;设置后它们也会在行边界匹配。如果多行模式匹配得太多或太少,先从这两个标志入手检查。

相关工具