正则测试
实时高亮匹配、展示捕获组,支持 g/i/m 开关
正则表达式测试:标志位、贪婪匹配与回溯陷阱
三个标志位在改变什么
| 标志 | 作用 | 常见误解 |
|---|---|---|
g 全局 | 找出所有匹配,而不是只找第一个 | 它有状态,见下一节 |
i 忽略大小写 | 字母大小写不敏感 | 它同时也让字符类的大小写扩展(如 [a-z] 会匹配大写) |
m 多行 | ^ 和 $ 匹配每一行的开头与结尾,而不是整个字符串的首尾 | 它就是让你能"逐行匹配"的开关;不加它 ^ 只匹配全文开头 |
第三行是最常被忽略的。^ERROR 在不加 m 时只能匹配整个文本的第一行开头;ERROR 出现在第 100 行时不会命中。加上 m 之后,每一行都被当成独立的行来处理。
另外还有一个常被忘记的标志 s(dotAll):让 . 也能匹配换行符。默认情况下 . 不匹配换行,所以想匹配跨行内容时 .* 会失效——这是"为什么我的跨行匹配不工作"的头号原因。
g 标志是有状态的
这是一个会造成"同一个正则时对时错"的真实陷阱。带 g 的正则对象会保留一个内部位置指针(lastIndex):
- 第一次执行匹配成功,指针停到匹配结束的位置。
- 第二次执行从那个位置继续往后找,而不是从头开始。
- 如果后面找不到匹配,指针重置为 0;但如果找到了,指针继续前移。
后果:在循环里对多个字符串复用同一个带 g 的正则时会漏掉匹配。正确做法是每次匹配新建正则对象,或者在复用前手动把 lastIndex 归零。判断依据是"这个模式在单个字符串里出现过一次还是多次"——只判断存在性时不该加 g。
贪婪与懒惰:为什么匹配比预期长
量词默认是贪婪的:尽量多匹配。加上 ? 变成懒惰:尽量少匹配。
| 输入 | 模式 | 结果 |
|---|---|---|
<a>1</a><a>2</a> | <a>.*</a> | 整个字符串(贪婪,越过第一个 </a> 继续到最后一个) |
| 同上 | <a>.*?</a> | <a>1</a>(懒惰,第一个就停) |
提取"每一个标签内容"时必须用懒惰,否则只能拿到第一个到最后一个之间的一大段。但懒惰也会带来回溯代价,见下一节。
灾难性回溯:正则能让浏览器卡死
嵌套量词会产生指数级的回溯。经典例子:用 (a+)+$ 去匹配一长串 a 后面跟一个 b(如 aaaaaaaaaaaaaaaaaaaaaaaaX)。
引擎会尝试所有"如何把 a 分组"的组合,数量随长度指数增长。20 个 a 尚可,30 个就可能让页面无响应。
这类模式在真实项目里的形态通常是:(.*)*、([a-z]+)*、多个 .* 夹着可选部分。防御方式:
- 去掉嵌套量词。
(a+)+和a+在功能上等价,后者没有回溯风险。 - 用具体字符类替代
.。[^"]*比.*明确得多,也让引擎有更多信息剪枝。 - 加锚点。有
^或具体前缀时,失败的匹配能更早被排除。 - 限制输入长度。用户可提交的长文本不应该直接丢给复杂正则——这是可被利用的拒绝服务入口(ReDoS)。
- 用工具实测。本工具在匹配时会显示匹配与捕获组,跑一次长输入就能看出是否卡顿。
常用构造速查
| 写法 | 含义 |
|---|---|
\d \w \s | 数字 / 单词字符 / 空白(大写形式表示取反) |
[abc] [^abc] | 字符集合 / 排除集合 |
^ $ | 行首 / 行尾(配合 m 时逐行生效) |
\b | 词边界,用来避免 id 命中 userId |
(?:...) | 非捕获组,只分组不占用编号 |
(?<name>...) | 命名捕获组 |
(?=...) (?!...) | 先行断言:后面必须 / 不能出现某内容 |
(?<=...) (?<!...) | 后行断言:前面必须 / 不能出现某内容 |
字符类内部有两条容易忘的规则:. 在 [] 里就是字面点(不需要转义);而 - 放在开头或结尾也是字面减号,放在中间才表示范围。
正则不适合解决的两类问题
- 解析 HTML / XML。这类格式是嵌套结构,正则没有递归能力(语法上无法表达"匹配配对的括号")。除极端受限的场景外,应该用真正的解析器——本工具集里就有 XML 转换工具用的是浏览器内置解析器,那才是正确做法。
- 校验邮箱、URL 的"完全合规"。邮箱地址的完整语法允许带引号的本地部分、注释、IP 字面量域名等,写一个完全合规的正则会长到无法维护,而实践中能用的简短正则会漏掉一些合法地址。正确的做法是用一个实用模式做基本校验(有 @、有域名点、无空格),把真正的验证交给"发一封确认邮件"。不要试图用正则判断"这个邮箱存不存在"。
工具支持实时高亮匹配、展示每个匹配及其捕获组内容,并提供 g/i/m 开关,方便逐个验证模式的行为。测试在浏览器本地完成。
常见问题
为什么高亮有时只标了一处?
没勾选 g(全局)时正则只匹配第一个结果。勾选 g 后会高亮所有匹配。
捕获组为空显示什么?
未参与匹配的组会显示为"undefined",这通常发生在可选组未匹配时,属正常现象。