正则表达式测试工具

在分隔符 / / 之间输入表达式,匹配会随着输入实时高亮:g i m s u y 用开关切换,每处命中都会列出对应的编号与命名捕获组,替换结果也可直接复制。

在浏览器本地运行 开发者工具

高亮预览 位置标尺 —— ^ 之后是 行:列

替换遵循 JavaScript String.replace 语义:$& 表示整段匹配,$1…$99 是编号捕获组,$<name> 是命名捕获组;像 $$ 这样的特殊序列由引擎解析成一个字面量 $。替换结果始终是纯文本,不会被当作 HTML。

替换结果
 

匹配结果

按当前表达式与修饰符找到的每一处匹配及其捕获组
序号 索引 匹配内容 捕获组
正则表达式速查表
. \. .*
点号匹配除换行符之外的任意单个字符,加上 s 修饰符后才能跨行。它并不是普通句点,所以 1.49 同样会命中 1x49;要匹配小数点请写成 1\.49。
\b
单词边界是零宽断言:只判断位置、不消耗字符。\bcat\b 能匹配 cat 和 cat.,但不会匹配 category。由于下划线属于单词字符,\bfoo\b 在 my_foo 中并不会命中 —— 这正是边界莫名失效的常见原因。
\d \w \s
这三个简写各自匹配一个字符,分别对应数字、单词字符(字母数字下划线)和空白,大写形式 \D \W \S 是取反。在 JavaScript 里它们都只覆盖 ASCII:\w 不会匹配汉字或带重音的字母,处理中文请加上 u 后改用 \p{L} 或 \p{Script=Han}。
^ $
锚点。不带 m 时它们只对应整段文本的首尾,因此在别处按行调试好的表达式到这里会看起来不生效。另外 JavaScript 比 Python 更严格一点:$ 只匹配真正的结尾,不会匹配末尾换行符之前的位置 —— 请先去掉尾部换行,或者加上 m。
贪婪与懒惰
量词默认是贪婪的:.* 会尽量多吃,只要剩下的部分还能匹配,所以 "<.*>" 作用于 a<b>c<d>e 时会把两个标签一起吞掉。后面再加 ? 变为懒惰 —— "<.*?>" 只到第一个 > 就停 —— 但懒惰只代表尽可能少,不等于正确,通常写成排除型字符集合 "<[^>]*>" 更快也更清晰。
[ ]
字符集合只匹配一个字符:[abc]、[a-z]、[^0-9]。方括号内 . | ( ) 都不再有特殊含义,- 用于表示区间(要匹配连字符请把它放在最前或最后),只有 \、]、^、- 需要小心。集合本身仍只是一个字符,要重复请写 [0-9]+。
( ) (?<name>) (?:)
普通圆括号既分组又捕获,编号按左括号出现的顺序确定,可在替换里用 $1 引用。(?<year>…) 给组起名,让 $<year> 比 $2 更好读;(?:…) 只分组不捕获,当你只是为了包住一个分支结构时就该用它。
a | b
选择运算符的优先级最低,因此 cat|dog food 实际是匹配 cat 或者 dog food,而不是 cat food 与 dog food。请把真正想分组的内容括起来:(?:cat|dog) food。
(?=) (?!)
前瞻只检查后面的内容、不消耗字符,是把多个条件组合起来的常用写法:(?=.*\d)(?=.*[A-Z])\S{8,} 读作至少一个数字、一个大写字母、总长八位以上。主流 JavaScript 引擎也已支持后顾 (?<=…) 与 (?<!…)。
$& $1 $<name> $$
替换串里 $& 是整段匹配,$1…$99 是编号捕获组,$<name> 是命名捕获组,$$ 会被折叠成一个字面量 $。如果引用的组在表达式里并不存在,这段记号会原样保留;替换结果始终是纯文本,不会被解析成 HTML。

使用方法

  1. 在「正则表达式」框中输入表达式本体——两侧的斜杠只是为了看得清楚而显示,请不要输入斜杠;也可以直接点击「载入示例」,用一组真实日志和已经能命中它的表达式开始。
  2. 切换 g、i、m、s、u、y 开关。每个开关都会显示自己的字母,下方提示行会说明当前生效的修饰符各自改变了什么,你的选择下次打开时仍然保留。
  3. 查看测试文本下方的「高亮预览」和位置标尺(^ 行:列),再对照「匹配结果」表格:每处匹配一行,包含索引、匹配内容与捕获组,未参与的组会明确显示为 (未匹配)。
  4. 填写「替换内容」即可看到替换结果;选中表达式片段后点击「转义特殊字符」可自动转义元字符,最后用「复制」取出结果,或点击「清空」重新开始。

常见问题

为什么正则在这里能匹配,在我代码里却不行?

这里运行的是浏览器内置的 ECMAScript 正则引擎,并不是 PCRE 或 Python。具体差别有:\d、\w、\s 只覆盖 ASCII,匹配中文或带重音的字母需要加上 u 并改用 \p{L};$ 只匹配整段文本的真正结尾,不像 Python 那样还能匹配末尾换行符之前;命名组写法是 (?<name>…) 和 \k<name>,Python 的 (?P<name>) 会直接报错;把带 // 分隔符的整串复制过来也会匹配不到。另外在 Python 源码里字符串本身会吃掉反斜杠,请写成 r"\d+"。

为什么一处都匹配不到?

常见原因有三类。其一是修饰符:不加 m 时 ^ 和 $ 只对应整段文本的首尾,按行调试好的表达式遇到多行文本就会失效,不加 g 则只看第一处。其二是贪婪:.* 会尽量多吃,"<.*>" 作用于两个标签时会把中间全部内容吞掉,应改用 "<.*?>",更好的写法是 "[^>]*"。其三是优先级:选择运算符最低,cat|dog food 其实只匹配 cat 或 dog food。若表达式本身语法有误,状态行会给出引擎原始报错并附排查提示;而匹配过宽的表达式会被限制渲染数量,不会让页面卡死。

我的表达式和文本会被传走吗?

不会。匹配、高亮和替换全部在当前页面内由浏览器自身的正则引擎完成,组件没有任何运行时依赖,也不会发起网络请求,页面加载后断网依然可用。因此用它测试包含真实邮箱、IP 或令牌的日志与接口数据同样是安全的。

指南

相关工具