正则表达式测试工具
把 JavaScript 正则表达式应用在示例文本上进行测试,实时高亮匹配、捕获组、命名捕获组,并提供替换预览。全部在你的浏览器中运行。
- 文件永不离开你的设备
- 免费,无需注册
- 无水印
- 加载后可离线使用
如何正则表达式测试工具
- 1
输入你的模式
输入表达式,选择你需要的标志。
- 2
添加测试文本
匹配结果会随输入实时高亮,每个捕获组都会列出来。
- 3
尝试替换
替换选项卡会用 $1 这样的引用方式预览替换结果。
为什么实时高亮比在脑子里读一遍模式更可靠
一个正则表达式很难在脑子里对照真实文本去推演——像 `(\d{3})-(\d{2})-(\d{4})` 这样的模式,作为一段抽象描述读起来还算清楚,但要确认它恰好匹配了预期的那些子字符串、而且只匹配那些,针对一段具体的示例文本去核实,是和单纯阅读模式、在脑子里推理完全不同的一种检查。在输入模式的同时,直接在示例文本上高亮每一处匹配,把这种抽象的推理变成了一种具体、即时的视觉确认——匹配要么出现在预期的位置,要么没有,不需要任何脑内模拟。
这一点对任何用到前瞻断言、后顾断言或非贪婪量词的场合尤其有价值,因为在这些情况下,实际匹配到的边界往往和单纯读模式所猜测的不一样。直接看到一个匹配从哪里开始、到哪里结束,而不是靠猜测,能立刻发现一个差一位的边界错误,而不是等它已经在一个校验函数里上线之后才发现。
JavaScript 正则表达式和 PCRE 或 Python 的 re 不是同一门语言
正则表达式的语法在不同语言之间看起来大体相似,但具体支持哪些特性,一旦模式用到基础功能之外的东西,差异就会变得很重要。JavaScript 没有独占量词和原子组,这两者在 PCRE(被 PHP 和许多其他工具使用)里是明确用来防止某些回溯模式的手段。后顾断言现在 JavaScript 已经支持了,但它比前瞻断言晚出现了相当长一段时间,在较老的浏览器版本里根本不可用,这一点对需要运行在老旧环境上的代码来说偶尔仍然重要。而 JavaScript 模式里的 `\d` 只匹配 ASCII 数字 0–9,除非模式同时带有 `u`(Unicode)标志,这会让任何假设它和另一门语言正则引擎里支持 Unicode 的数字类行为一致的人感到意外。专门针对 JavaScript 测试一个模式,而不是照搬一个为 Python 或某个 shell 工具写的示例,正是这个工具的设计出发点。
灾难性回溯,以及为什么这个测试工具设置了时间预算
某些正则表达式模式——最常见的是带有嵌套量词的模式,比如 `(a+)+b`——存在一种病态的特性:面对一个最终不匹配的长字符串,引擎在放弃之前会尝试指数级增长数量的回溯方式。一个在十个字符的不匹配字符串上瞬间返回结果的模式,在一个五十个字符的字符串上可能需要比宇宙年龄还长的时间——这不是夸张,而是指数增长这一数学行为的字面写照。这是生产系统里一类真实的、可被利用的漏洞——ReDoS,即正则表达式拒绝服务攻击——攻击者只要能控制传给一个存在漏洞的模式的输入字符串,就能用单个请求让一个服务器进程无限期挂起。
这个工具会在一段较短的时间预算之后中止求值,专门是为了让带有这种特性的模式只会让测试会话卡住,而不是让整个浏览器标签页彻底冻结,而触发这个中止本身就是有用的信息:这意味着这个模式很可能存在这个具体的漏洞,需要重新设计,通常的做法是让内层的量词更具体一些,从根源上避免引发指数级回溯的那种歧义。
常见问题
这是哪种正则表达式方言?
JavaScript(ECMAScript)方言,也就是你浏览器里运行的那种。它在一些地方和 PCRE、Python 不一样——在非常老的浏览器里没有后顾断言,没有独占量词或原子组,而且 `\d` 只匹配 ASCII 数字,除非你加上 `u` 标志。
全局标志实际上改变了什么?
不加 `g` 的话,只会找到第一个匹配。加上它,就会返回每一个匹配——这正是在整份文档里做高亮或替换时你想要的效果。这个工具无论如何都会显示所有匹配项,但这个标志仍然会影响 `replace` 的行为。
为什么我的模式超时了?
有些模式会发生灾难性回溯——像 `(a+)+b` 这样的嵌套量词,在一个不匹配的字符串上可能会花费指数级的时间。这个工具会在一个较短的时间预算之后中止,而不是让你的标签页卡死。如果发生这种情况,解决办法几乎总是让内层的量词更具体一些。
怎么在替换里使用捕获组?
用 `$1`、`$2` 以此类推来引用它们,命名捕获组 `(?<name>…)` 则用 `$<name>` 引用。用 `$&` 表示整个匹配,`$$` 表示字面意义上的美元符号。