正则测试指南:实时看匹配、分组和坑
正则写错了,看结果比读文档快。把规则和一段真实文本放进去,一边改一边看高亮命中,能匹配到哪里、漏在哪里、分组取出来是什么,一目了然。
什么时候值得先测一下
从日志里批量提取 IP、订单号、时间戳这类有固定形态的数据时,正则几乎一次写不对。先在样例上跑通,比写进脚本再反复重跑快得多。表单校验也一样:邮箱、手机号、车牌这类规则,边界情况(末尾空格、全角字符、前导零)只有拿真实数据试才暴露得出来。
还有一种是接手别人的代码:看到一串没有注释的正则,直接粘进测试框,看它到底命中什么、有哪些分组,比自己脑内推演靠谱。做批量替换之前,也建议先看看会命中多少处,免得一次替换毁掉整个文件。
怎么用才不出错:方言、标志、边界
先确认方言。这里是浏览器的 JavaScript 正则引擎,和 PCRE、Python、Java 有细微差别:JS 的 \d 只匹配 ASCII 数字,\w 只匹配 ASCII 字母数字下划线(加 u 标志也不改变这一点),命名分组写法和回溯行为也各有特点。在这里能过,不代表能在 grep -P 里过。
再看标志位。g 是全局,不加只返回第一个命中;i 忽略大小写;m 让 ^ 和 $ 匹配每一行的开头结尾而不是整个字符串;s 让点号也能匹配换行。最常踩的坑是 m:不加的话,一段多行文本里只有第一行能被 ^ 命中。
- 准备两组样例:必须命中的、必须不命中的。只测前者,等于只测了一半
- 不需要取值的分组写成 (?:...),分组太多时结果区很难读
- 把贪婪的 .* 换成懒惰的 .*? 或更精确的字符类,能少很多意外
- 先想清楚要不要用 ^ 和 $ 锚定,否则子串命中会骗人
局限:能匹配不等于该用正则
最大的风险是灾难性回溯。像 (a+)+$ 这种嵌套量词,遇到一长串「差一点就能匹配」的文本时,耗时呈指数增长。浏览器的正则引擎没有超时机制,页面会直接卡死,只能关标签。这个工具设了 5000 次命中的上限,防的是结果太多,防不了回溯本身。
其次,正则不适合解析嵌套结构。HTML、JSON、XML 都有专门的解析器,用正则去配标签,遇到嵌套和注释就崩。邮箱、URL 的完整规范也不是一个正则能覆盖的,实用做法是按业务需要写个宽松规则,再发确认邮件验证。正则跑在大文本上也很吃内存,几 MB 以上建议先切分再匹配。
常见问题
为什么同一个正则在工具里能匹配,在代码里不行?
先查三件事:方言是否一致(这里是 JS 引擎,Python、PHP、Java 的写法有差别)、标志位是否一致(尤其 g 和 m)、以及代码里是否多转义了一层。从 JSON 或 YAML 里读正则时,反斜杠常常被转义两次,\d 变成字面上的反斜杠加 d,匹配的就不是数字了。
贪婪和非贪婪怎么区分?
贪婪的 .* 尽可能往后吃,懒惰的 .*? 尽可能少吃。对 <b>甲</b><b>乙</b> 这段文本,<b>.*</b> 会一次吃到最后一个 </b>,而 <b>.*?</b> 会分别命中两组。默认是贪婪,需要最短匹配时加问号。
嵌套量词一定会卡死页面吗?
不一定,取决于输入。(a+)+$ 对一串连续的 a 几乎立刻成功,对「一长串 a 再加一个 b」这种几乎匹配却不匹配的输入才会爆炸。所以测试要用近似但不匹配的样例,只拿手边那段正常数据试是测不出来的。
相关工具:正则测试