关于 正则表达式在线测试
正则表达式测试器用浏览器自带的 RegExp 引擎编译你的表达式,并在一个 JSON 对象里给出:命中了多少次、每次命中的文本与偏移量、按序号和按名称的捕获组,以及把替换模板作用到整段文本后的结果。
这里的引擎是 ECMAScript,不是 PCRE。后顾断言、具名分组、Unicode 属性转义都支持,但原子组、占有量词、递归、(?i) 这类内联修饰符,以及 \A、\z、\K 在 JavaScript 里根本不存在。从 PHP、Python 或 Perl 代码里搬过来的表达式,要么编译不过,要么更糟——编译通过了,含义却变成另一回事。动手之前先确定它最终跑在哪个引擎上。
如何使用这个开发者工具
- 在「表达式」输入框里只填表达式本体,不要带定界斜杠。
- 在「标志」输入框里填裸字母,例如 gm 或 giu。
- 把测试文本粘进来,既要有应当命中的行,也要有那些「差一点就像」但必须被拒绝的行。
- 运行测试,读取报告:先看 count,再逐条看偏移量、按序号的捕获组和具名捕获组。
- 等匹配边界确认无误后再调替换模板;$1 是第一个分组,$<name> 是具名分组,$& 是整个匹配,$$ 是字面美元符号。
实例:从日志行里抽取服务名与级别
三行日志,其中只有两行应当命中。具名分组让报告更易读,替换模板则把每一行命中的内容重写掉。
Pattern: (?<level>WARN|ERROR)\s+\[(?<svc>[\w-]+)\]\s+(?<msg>.+)
Flags: gm
Replacement: $<svc>/$<level>
Test text:
2026-03-04T10:15:02Z INFO [auth-api] token refreshed
2026-03-04T10:15:07Z WARN [auth-api] retry 1 of 3
2026-03-04T10:15:09Z ERROR [billing] upstream timeout after 30s{
"count": 2,
"matches": [
{
"match": "WARN [auth-api] retry 1 of 3",
"index": 75,
"groups": [
"WARN",
"auth-api",
"retry 1 of 3"
],
"namedGroups": {
"level": "WARN",
"svc": "auth-api",
"msg": "retry 1 of 3"
}
},
{
"match": "ERROR [billing] upstream timeout after 30s",
"index": 126,
"groups": [
"ERROR",
"billing",
"upstream timeout after 30s"
],
"namedGroups": {
"level": "ERROR",
"svc": "billing",
"msg": "upstream timeout after 30s"
}
}
],
"replaced": "2026-03-04T10:15:02Z INFO [auth-api] token refreshed\n2026-03-04T10:15:07Z auth-api/WARN\n2026-03-04T10:15:09Z billing/ERROR"
}偏移量 75 和 126 是在测试文本中从零开始计数的位置,每个换行符算一个字符。注意 msg 停在了本行末尾:没有加 s 标志,所以 .+ 无法跨过行终止符——面向行的输入正需要这种行为。INFO 那一行从未命中,因此在替换结果中原样保留。
标志、字符类与量词
第三列的每一条都针对本测试器使用的 JavaScript 引擎,其中几条在 PCRE 和 Python 的 re 模块里表现并不相同。
| 记号 | 作用 | 容易踩的坑 |
|---|---|---|
| g | 命中第一次之后继续向后扫描 | 本工具列出匹配时总是按全局处理;替换却不是,没有 g 时只会重写第一处 |
| i | 忽略大小写 | 与 u 同用时按 Unicode 大小写折叠,/k/iu 能匹配开尔文符号 U+212A,而 /k/i 不能 |
| m | 让 ^ 和 $ 也在行边界处匹配 | \r、\u2028、\u2029 同样是行终止符,所以 ^.*$ 作用于 CRLF 文本时,会在 \r 与 \n 之间多出一次空匹配 |
| s | 让 . 也能匹配行终止符 | 不加它时,/a.b/ 匹配不上「a、换行、b」这样的两行文本 |
| u | Unicode 模式:\p{...}、\u{...}、按码位处理 | 不加 u 时 \p{L} 并不报错,而是静默地去匹配字面文本 p{L} |
| y | 每次尝试都锚定在 lastIndex 上 | 配合 g 使用时,扫描会在第一个匹配失败的位置停下,所以 aa b aa 只得到一次命中 |
| . | 除行终止符以外的任意字符 | 只要匹配需要跨行,就得加上 s |
| \d 与 \w | ASCII 数字,以及 [A-Za-z0-9_] | 即便加了 u 它们仍然只认 ASCII;需要 Unicode 请用 \p{Nd} 和 \p{L} |
| \s | 空白字符 | 与 \d、\w 不同,它确实覆盖 Unicode 空白,包括 U+00A0 和字节序标记 |
| \b | 单词边界,其定义建立在 \w 之上 | 因此在带音标的字母和中日韩文本周围,它的行为并不符合直觉 |
| a* a+ a{2,} | 贪婪:先尽量多吃,再往回吐 | /<.+>/ 作用于 <a><b> 时匹配整串,而不是第一个标签 |
| a*? a+? a{2,}? | 懒惰:先尽量少吃,不够再往后要 | /<.+?>/g 作用于 <a><b> 时先匹配 <a>,再匹配 <b> |
常见问题排查
一运行测试,整个标签页就卡住了。
成因 灾难性回溯。像 (\s*\w+)+$ 这样在量词内部又套量词、且两者字符集互相重叠的表达式,遇到「差一点就能匹配」的输入时,引擎必须穷举指数级的切分方式才能确认它匹配不上。
解决办法 绝不要在字符集重叠的地方嵌套量词。分隔符之间的 .* 改写成 [^,]* 这类取反字符类,并给表达式加锚点让失败尽早判定。如果只加几个字符耗时就陡增,这条表达式就是指数级的。
带点号的表达式到行尾就停了。
成因 在 JavaScript 中,除非设置 s 标志,否则点号不匹配行终止符,而 \r 和 \n 一样算作终止符。
解决办法 需要单次匹配跨行时加上 s。面向行的输入则要刻意不加 s——正是它拦住了贪婪的 .+ 把整个文件吞掉。
Unicode 属性转义什么都匹配不到,或者匹配错了,却没有任何报错。
成因 只有在 Unicode 模式下 \p 才是属性转义。不加 u 时它退化为同一性转义,\p{L} 会被编译成 p{L} 这几个字面字符,然后去匹配这段文本。
解决办法 只要用到 \p{...}、\u{...},或者需要把表情符号这类星位字符当作一个单位,就在标志里加 u。同时要知道 u 也会把一些原本被容忍的写法变成语法错误,例如量词之外落单的 {。
加了锚点的表达式只匹配到测试文本的第一行。
成因 不加 m 标志时,^ 和 $ 锚定的是整个字符串的开头与结尾,而不是每一行,因此只有第一行能满足 ^。
解决办法 加上 m,锚点才会在行边界处生效。注意把 m 和 s 分清楚:m 改变的是 ^ 和 $ 在哪里匹配,s 改变的是点号能否跨过换行。
匹配列表里有好几次命中,替换结果却只改了第一处。
成因 工具在枚举匹配时强制加上了全局标志,所以你总能看到全部命中。而替换使用的是你输入的原始标志,没有 g 的 String.replace 只会重写第一处。
解决办法 需要批量替换时把 g 写进标志输入框。这件事的价值超出本工具本身:在应用代码里把同一条表达式既用于判断又用于替换时,同样的不对称会咬人。
适合用这个工具的场景
- 证明一条日志解析或校验表达式确实命中了该命中的行,也确实拒绝了不该命中的行。
- 在代码里按下标取捕获组之前,先看清每个分组实际的起止边界。
- 在不碰真实文件的前提下预演批量查找替换,包括带具名分组的替换模板。
- 为某个正则缺陷构造一份可复现的小样例,连同表达式一起放进测试用例。
应该换用别的工具的场景
- 解析 HTML 或 XML。嵌套结构不是正则语言,请改用 HTML 预览工具或 XML 校验工具。
- 按 RFC 5322 完整文法校验邮箱地址。判断有且只有一个 @ 和一个像样的域名就够了,剩下的交给一封确认邮件。
- 找出两段文本之间的差异,那件事文本比较工具做得更专业。
隐私与数据处理
表达式的编译与匹配都在当前浏览器标签页内完成,不传输也不保存。真正的风险来自测试数据而不是表达式:写日志解析规则的人往往直接粘贴真实日志片段,而那些行里带着 IP 地址、会话标识,偶尔还有凭据。请把样本裁剪到够用即可的那几行。
相关开发者工具
常见问题
表达式要带定界斜杠吗?
不要。表达式输入框只接受表达式本体,标志有自己的输入框。如果粘进 /^abc$/i,首尾的斜杠会变成表达式要寻找的字面字符,这正是「表达式突然一个都匹配不到」的常见原因。
这个引擎兼容 PCRE 吗?
部分兼容,而不兼容的那部分很要命。先行断言、后顾断言、具名分组和 Unicode 属性转义都可用;原子组、占有量词、递归、内联标志修饰符,以及 \A、\z、\K 在 JavaScript 里并不存在。请在最终会执行它的引擎上测试。
贪婪量词和懒惰量词有什么区别?
贪婪量词先尽量多吃文本,再一个字符一个字符往回吐,直到表达式剩下的部分能对上。懒惰量词则先尽量少吃,不够再往后扩。对文本 <a><b> 来说,<.+> 匹配整串,而 <.+?> 分别匹配出 <a> 和 <b>。
替换模板里怎么写一个字面美元符号?
写 $$。在替换模板中,美元符号用于引入替换项:$1 是第一个分组,$<name> 是具名分组,$& 是整个匹配。$$ 则转义回单个字面美元符号。
支持后顾断言吗?
支持。后顾断言随 ES2018 引入,当前的 Chrome、Firefox 以及 Safari 16.4 及以上版本都可用。JavaScript 的后顾断言还允许变长,这与若干要求固定宽度的引擎不同,因此在这里合法的写法换个引擎未必接受。
v 标志是做什么的?
v 由 ES2024 标准化,是 u 的严格超集,在字符类内部增加了集合运算——用 && 取交集、用 -- 取差集,并支持多字符的字符串属性。它与 u 不能互换,因为 v 还要求类内更多字符必须转义。