正则表达式

匹配、捕获和替换文本

正在加载工具…

关于 正则表达式在线测试

正则表达式测试器用浏览器自带的 RegExp 引擎编译你的表达式,并在一个 JSON 对象里给出:命中了多少次、每次命中的文本与偏移量、按序号和按名称的捕获组,以及把替换模板作用到整段文本后的结果。

这里的引擎是 ECMAScript,不是 PCRE。后顾断言、具名分组、Unicode 属性转义都支持,但原子组、占有量词、递归、(?i) 这类内联修饰符,以及 \A、\z、\K 在 JavaScript 里根本不存在。从 PHP、Python 或 Perl 代码里搬过来的表达式,要么编译不过,要么更糟——编译通过了,含义却变成另一回事。动手之前先确定它最终跑在哪个引擎上。

如何使用这个开发者工具

  1. 在「表达式」输入框里只填表达式本体,不要带定界斜杠。
  2. 在「标志」输入框里填裸字母,例如 gm 或 giu。
  3. 把测试文本粘进来,既要有应当命中的行,也要有那些「差一点就像」但必须被拒绝的行。
  4. 运行测试,读取报告:先看 count,再逐条看偏移量、按序号的捕获组和具名捕获组。
  5. 等匹配边界确认无误后再调替换模板;$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」这样的两行文本
uUnicode 模式:\p{...}、\u{...}、按码位处理不加 u 时 \p{L} 并不报错,而是静默地去匹配字面文本 p{L}
y每次尝试都锚定在 lastIndex 上配合 g 使用时,扫描会在第一个匹配失败的位置停下,所以 aa b aa 只得到一次命中
.除行终止符以外的任意字符只要匹配需要跨行,就得加上 s
\d 与 \wASCII 数字,以及 [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 还要求类内更多字符必须转义。