🔍 正则表达式测试器

实时匹配 · 分组高亮 · 替换拆分 · 逐段拆解 · 纯本机运行
🔧 正则表达式 写错了会当场告诉你错在哪
/ /
标志
还没有写正则。
📝 测试文本 整段一起匹配
粘一段文字进来,下面的结果会实时跟着变。勾上「逐行测试」就是把每一行当成独立的测试样本。
📊 匹配结果
🎯命中:0
⏱️用时:—
🧩分组:0
📏文本:0
🫧还没有匹配结果
✨ 高亮预览 橙色是整段命中,里面的小色块是分组,「│」是长度为 0 的空匹配
测试文本是空的。
♻️ 替换
替换串里可以写 $1、$2 引用第几个分组,$& 代表整段命中,$<名字> 代表命名分组,$$ 是字面的美元符号。跟 JavaScript 的规则完全一样。
替换结果会显示在这里。
✂️ 拆分
用这个正则把测试文本切开。这是 split 的效果,跟是否勾 g 无关。正则里带括号的话,JS 还会把括号抓到的内容也插进结果里,这里标成「组1、组2」。
拆分结果会显示在这里。
🧩 表达式拆解 一段一段看这个正则由什么组成
这是给人看的说明,不是执行结果。写法千变万化,个别地方只能给个大概,但结构不会错。
🧾写个正则就会自动拆开给你看
📚 常用片段 点一下直接填进去
都是常见场景里能用的写法,但业务规则各不相同,套到自己数据上之前先拿真实样本试一遍。
🧠
真跑一遍用的就是浏览器自带的正则引擎,跟你在代码里写的是同一套规则
⏳
不会卡死匹配放到后台线程里跑,超过两秒算超时并提醒你回溯太多
🎨
分组看得清整段命中和每个捕获分组用不同颜色标出来,0 长度的空匹配画成一根竖线
🔀
替换和拆分替换串支持 $1、$&、$<名字>,拆分结果一段一行
📋
结果能带走正则、测试文本、替换结果、全部匹配都能一键复制
使用说明与常见问题

这里用的是哪种正则?

用的是 JavaScript 的正则(ECMAScript 语法),也就是浏览器和 Node.js 认的那一套:\d、\w、(?: )、(?<名字> )、(?= )、(?<= )、\p{…}(要配合 u 或 v;不带这两个标志也不会报错,只是被当成普通字母 p 加上字面的 {…},所以拿它匹配中文会一条都不中)这些都支持。Python、Java、PHP 的那几套不完全一样,而且有几个坑是单向的:Python 的 (?P<名字> ) 这里不认,得改写成 (?<名字> );原子组 (?>a)、独占量词 a*+、把标志写在括号里的 (?i) 这三种写法在这里会直接报语法错;反过来,这里的 \d 只认 0-9,Python 的 \d 连阿拉伯数字 ١ 也算数字;这里的 /a$/.test('a\n') 是 false,Python 的 $ 默认能匹配到结尾那个换行前面。这里的 u、v、d 三个标志,别的语言也未必有。

为什么写着写着突然提示超时?

只有一种情况会超时:这个正则回溯得太厉害。典型写法是 (a+)+b 这种——外面套了多层重复,而且最后匹配不上:拿 (a+)+$ 去试 26 个 a 再加一个 c,引擎会把「这串 a 怎么切成几段」的所有组合都枚举一遍,a 每多一个耗时翻一倍:同一台浏览器实测 22 个 a 用 0.6 秒、24 个用 1 秒,26 个已经跑到两秒以上、被页面掐掉。页面把匹配放在后台线程里跑,超过两秒就掐掉并告诉你「回溯太多」,所以页面不会被卡死,但这个正则本身确实要改写。常见的改法是:把能确定的部分写死、别用嵌套重复、或者给重复次数加上限(比如 a{1,100})。

八个标志分别是什么意思?

g 找全部匹配(不勾就只报第一个);i 不分大小写;m 让 ^ 和 $ 按行算;s 让点号 . 也能吃掉换行;u 按 Unicode 解析,\u{1F600}、\p{…} 这类写法都要靠它——不勾 u 也不会报错,只是 \u{1F600} 会退化成字面的 u{1F600} 这几个字符、\p{Letter} 退化成字面的 p{Letter};y 粘住,从上次结束的位置接着找;d 给每个分组记下它在原文里的位置;v 是新版的 Unicode 模式,u 和 v 不能同时开。

不勾 g 为什么只找到一个?

因为不勾 g 的时候,正则只做一次匹配,找到第一个就停——这也是 JavaScript 自己的规矩。想要全部命中就把 g 勾上。另外 y(粘住)也有自己的脾气:它只从上一次结束的位置往后找,不是从整段文字的开头重新找。

分组里的颜色是怎么分的?

橙色是整段命中的部分,里面套着的彩色小块是各个捕获分组,编号就是 $1、$2 里的那个数字。命名分组会标上名字。分组配色一共 7 色循环使用,第 8 组会回到第 1 组的颜色,具体编号看小块上的悬浮提示和匹配结果里的分组行。算每组的位置要靠 d 标志,所以页面内部会替你把 d 带上再计算,勾不勾都不影响高亮;只有在你这台浏览器压根不认 d 的时候(那个开关会置灰并在旁边提示),高亮才只整段上色、不分组。

「逐行测试」是干什么的?

把测试文本按行切开,每一行当成一个独立的样本去匹配,列出哪几行命中、命中了几次、第一次命中了什么。适合批量核对:比如从日志里粘一大段,逐行看哪些行符合某个规则。注意它和整段匹配不一样——^、$ 在逐行模式下锚的是每一行,而不是整段文字。

替换串里的那些符号怎么写?

$1 到 $9 是第几个捕获分组,$& 是整段命中的内容,$` 是匹配之前的那段文字,$' 是匹配之后的那段,$<名字> 是命名分组,$$ 是一个真的美元符号。规则跟 JavaScript 的 String.replace 完全一致,所以这里试出来的写法可以直接搬进代码。

拆解那段说明准吗?

结构是准的:分组、量词、字符类、锚点、断言都会分开列出来。但它只是给人看的说明,不是执行依据——比如 [a-z0-9] 这种字符类,它会告诉你「匹配 a 到 z 或 0 到 9 里的一个」,但具体怎么写取决于你自己。正则到底匹配出什么,只看上面的匹配结果。

「│」是什么?为什么有 0 字符的命中?

那是空匹配:正则确实匹配上了,但吃掉的内容长度为 0。边界类写法 \b、\B、断言 (?= )、(?! ) 和带 * 的重复都可能这样。它们有用,比如片段里那条 \B(?=(\d{3})+(?!\d)) 就是靠空匹配定位「该插千分位逗号的地方」。高亮区里没法给 0 宽度的内容上色,所以那种位置画成一根红色的「│」,鼠标悬上去能看到它排在第几个字符。

「位置 12 – 18」数的是字符还是字节?

都不是,它数的是 JavaScript 自己的码元(code unit):英文、汉字、全角标点都算 1,emoji 和少数生僻汉字算 2。所以「🙂a」里的 a 位置是 2 不是 1。这不是这页的算法问题,JavaScript 的 matchAll、index 给你的就是这个数,抄进代码里也是它。「文本」那一栏同样按码元算。

命中太多会不会把页面卡住?

不会一口气画完。引擎最多算 5,000 条命中(再多就停下并告诉你「太多,只算了前 5,000 个」);高亮区最多画前 1,500 处,剩下的位置写一句「剩下多少处看下面的匹配结果」;下面的卡片列表一屏先建 300 张,看完点底下那个按钮,每次再加 700 张。「复制全部匹配」不受这些限制,算出来的每一条都会给你。

我的文字会传到网上吗?

不会。整个页面是单个 HTML 文件,正则和测试文本都在你自己的浏览器里处理,一个字都不会发出去。刷新或关掉标签页,东西就没了——这页不保存任何历史记录,也没有服务器可以保存。(本站只统计页面被打开的次数,用的是一个 1×1 像素的计数请求,里面不含你的文字。)

为什么某个正则明明是对的却匹配不到?

最常见的是三个原因:① . 默认不匹配换行,文本里有换行就漏了,勾上 s;② ^ 和 $ 默认只管整段文字的开头结尾,勾上 m 才是按行算;③ 正则里没转义的 .、*、+ 这些是特殊符号,意思跟字面不一样。真要匹配它们本身,得写成 \.、\*、\+。

结果怎么拿走?

正则本身、测试文本、替换结果、拆分结果都能单独复制。「复制全部匹配」会按制表符分列给出序号、起止位置、整段命中和各个分组,粘到表格软件里能直接分列。