$1、$2 引用第几个分组,$& 代表整段命中,$<名字> 代表命名分组,$$ 是字面的美元符号。跟 JavaScript 的规则完全一样。split 的效果,跟是否勾 g 无关。正则里带括号的话,JS 还会把括号抓到的内容也插进结果里,这里标成「组1、组2」。用的是 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 的时候,正则只做一次匹配,找到第一个就停——这也是 JavaScript 自己的规矩。想要全部命中就把 g 勾上。另外 y(粘住)也有自己的脾气:它只从上一次结束的位置往后找,不是从整段文字的开头重新找。
橙色是整段命中的部分,里面套着的彩色小块是各个捕获分组,编号就是 $1、$2 里的那个数字。命名分组会标上名字。分组配色一共 7 色循环使用,第 8 组会回到第 1 组的颜色,具体编号看小块上的悬浮提示和匹配结果里的分组行。算每组的位置要靠 d 标志,所以页面内部会替你把 d 带上再计算,勾不勾都不影响高亮;只有在你这台浏览器压根不认 d 的时候(那个开关会置灰并在旁边提示),高亮才只整段上色、不分组。
把测试文本按行切开,每一行当成一个独立的样本去匹配,列出哪几行命中、命中了几次、第一次命中了什么。适合批量核对:比如从日志里粘一大段,逐行看哪些行符合某个规则。注意它和整段匹配不一样——^、$ 在逐行模式下锚的是每一行,而不是整段文字。
$1 到 $9 是第几个捕获分组,$& 是整段命中的内容,$` 是匹配之前的那段文字,$' 是匹配之后的那段,$<名字> 是命名分组,$$ 是一个真的美元符号。规则跟 JavaScript 的 String.replace 完全一致,所以这里试出来的写法可以直接搬进代码。
结构是准的:分组、量词、字符类、锚点、断言都会分开列出来。但它只是给人看的说明,不是执行依据——比如 [a-z0-9] 这种字符类,它会告诉你「匹配 a 到 z 或 0 到 9 里的一个」,但具体怎么写取决于你自己。正则到底匹配出什么,只看上面的匹配结果。
那是空匹配:正则确实匹配上了,但吃掉的内容长度为 0。边界类写法 \b、\B、断言 (?= )、(?! ) 和带 * 的重复都可能这样。它们有用,比如片段里那条 \B(?=(\d{3})+(?!\d)) 就是靠空匹配定位「该插千分位逗号的地方」。高亮区里没法给 0 宽度的内容上色,所以那种位置画成一根红色的「│」,鼠标悬上去能看到它排在第几个字符。
都不是,它数的是 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 才是按行算;③ 正则里没转义的 .、*、+ 这些是特殊符号,意思跟字面不一样。真要匹配它们本身,得写成 \.、\*、\+。
正则本身、测试文本、替换结果、拆分结果都能单独复制。「复制全部匹配」会按制表符分列给出序号、起止位置、整段命中和各个分组,粘到表格软件里能直接分列。