正则表达式速查表,免费

交互式正则表达式参考指南。

实时模式测试

测试模式

无匹配

使用方法

  1. 浏览模式分类,或使用搜索栏查找特定模式。
  2. 在「测试模式」中输入正则表达式,并在「测试文本」中输入示例文本。
  3. 切换标志(全局、忽略大小写、多行),即时查看高亮的匹配。

常见问题

什么是正则表达式?

正则表达式(regex 或 regexp)是一种用于搜索、查找和替换文本的模式。它使用特殊字符和语法来定义要匹配的字符串。

标志有什么用?

全局(g)查找所有匹配。忽略大小写(i)忽略字母大小写。多行(m)使 ^ 和 $ 匹配行的边界而非字符串的边界。

可以在我的代码中使用此速查表吗?

当然!一旦您测试好模式并确认有效,可直接将 regex 模式复制到您的 JavaScript、Python 或任何其他语言代码中。

模式语言简史

正则表达式起源于理论计算机科学。Stephen Kleene 在 1956 年关于神经网络的论文中定义了「正则集」;Ken Thompson 于 1968 年用 grep 把它们带进 Unix。Henry Spencer 的开源 regex 库(1980 年代中期)成为后来许多实现的基础。Larry Wall 在 Perl 中大幅扩展了语法,他的「Perl-compatible regular expressions」(PCRE)成为大多数现代语言追随的事实标准。如今存在几种紧密相关但又微妙不同的 regex 风味,在一个引擎里能跑的模式,在另一个引擎里不一定一模一样地跑。

你的模式所栖身的引擎

同一段语法在不同引擎里可能表示不同的意思。主要家族:

这份小抄的交互测试器在 JavaScript 中运行,所以模式由浏览器的 JS 引擎评估。在这里能跑的模式,在 Python 或 PHP 中可能表现不同。大多数差异在于高级功能(lookbehinds、Unicode property escapes、反向引用),而不是基本语法。

核心构件

几乎每一个 regex 模式都由这些元素构成:

值得记下来的模式

有一小撮模式出现得太频繁,值得放在脑子里:

用途模式
邮箱(基本)^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$
URLhttps?://[^\s]+
美国电话号码\(?\d{3}\)?[-.\s]?\d{3}[-.\s]?\d{4}
ISO 日期(YYYY-MM-DD)\d{4}-(0[1-9]|1[0-2])-(0[1-9]|[12]\d|3[01])
IPv4 地址(不验证八位组)\b\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\b
十六进制颜色^#?([0-9a-fA-F]{3}|[0-9a-fA-F]{6})$
行首/行尾的空白^\s+|\s+$
多个连续空格\s{2,}

关于邮箱 regex 的说明:完整的 RFC 5322 邮箱验证需要一段 6 000 字符的怪兽 regex。上面那条简单形式接受 99% 的真实邮箱,且不会拒掉任何合法邮箱;在生产环境中,与其想完美校验语法,不如发一封确认邮件。

贪婪 vs 懒惰:常见的意外

默认情况下,量词是贪婪的:它们会尽可能多匹配,同时仍让整体模式能够匹配。所以 <.+><a>text</a> 会匹配整段,而不仅仅是 <a>,因为 .+ 会抓尽可能多。要匹配最短可能的字符串,在量词后加 ?:<.+?> 会先匹配 <a>,再单独匹配 </a>。贪婪/懒惰的选择是「为什么我的 regex 没匹配到我期望的东西」这类 bug 的最常见来源之一。

灾难性回溯与 ReDoS

有些 regex 模式在某些输入上需要指数时间才会失败,这是一类被称为 ReDoS(Regular Expression Denial of Service)的拒绝服务漏洞。典型元凶是嵌套量词,比如 (a+)+(a|aa)+,作用在一长串 a 后面跟一个不匹配字符的输入上。引擎会在放弃之前尝试每一种切分字符串的方法,而方法数量是指数级的。

现实事故:Cloudflare 2019 年的故障由部署在 WAF 规则里的一段 regex 在某些输入上灾难性回溯触发。Stack Overflow 在 2016 年 7 月也遇到过类似事件:一段 post-trim regex(^[\s‌]+|[\s‌]+$)在一条包含约 20 000 个连续空白字符的评论上撞上了指数回溯,把站点击垮 34 分钟。防御性习惯:避免嵌套量词,在支持的地方优先使用 atomic groups((?>...)),并考虑对不可信输入使用 RE2 / 线性时间引擎。

值得知道的各语言怪癖

什么时候不要用 regex

Jamie Zawinski 1997 年的著名一句:「Some people, when confronted with a problem, think 'I know, I'll use regular expressions.' Now they have two problems.」

常见错误

  1. 忘了转义特殊字符。.*?+()[]{}\^$|/ 都有特殊含义。要按字面量匹配,前面加反斜杠。
  2. 贪婪量词吃太多。当你想要尽可能短的匹配时,加 ? 切换到懒惰匹配。
  3. 忘了全局标志,然后疑惑为什么只显示第一个匹配。没有 g 标志,JavaScript 的 String.prototype.match() 只返回第一个匹配。
  4. 长输入上的灾难性回溯。嵌套量词如 (a+)+ 在某些输入上会卡住。用边界用例测试。
  5. 假设同一个 regex 在每种语言里都行为相同。Lookbehinds、Unicode 转义、字符类简写都各有不同。
  6. 试图过严验证邮箱。技术上正确的 RFC 5322 regex 无法维护;简单 regex 加上注册时确认邮件,才是真正能跑的模式。
  7. 对 HTML、JSON 或 CSV 使用 regex。用一个合适的解析器;前期省下的时间会在 bug 上加倍还回来。

更多常见问题

为什么我的模式在这里能跑但在我的代码里失败?

最常见的原因是引擎差异。JavaScript 的 RegExp 不支持 PCRE 支持的某些功能(反之亦然)。常见坑:lookbehinds 很晚才加入 JS(ES2018),命名组语法略有不同,Unicode property escapes 需要 u 标志,POSIX 字符类如 [[:alpha:]] 在 JS 中基本缺席。在你将要部署的引擎中测试。

有「全局」跨多行匹配的方法吗?

两个标志一起用。m(多行)标志让 ^$ 匹配每一行的开头和结尾,而不是整段字符串的开头和结尾。s(dotall)标志让 . 也匹配换行字符。再配合全局 g,你可以写跨行模式来找出每一个匹配:/^foo.+$/gms

我的模式和测试文本会被发送到任何地方吗?

不会。模式匹配使用浏览器内置的 JavaScript RegExp 引擎;没有任何东西被上传到服务器。这在你针对真实生产日志数据、内部 API 响应或敏感内容测试模式时很重要。

我应该学 lookbehinds 吗?

有用但非必要。Lookbehinds 让你匹配前面有某物的文本,而不把那个「某物」包含进匹配里。例子:(?<=\$)\d+ 匹配美元符号后面的数字而不消耗那个美元符号。它们在 PCRE、现代 JavaScript(ES2018+)和 Python 的 regex 模块中受支持。如果你写的是可移植的模式,先检查目标引擎。

为什么用 (?:...) 而不是 (...)?

非捕获组((?:...))略快,不在捕获数组里占位置,让你的匹配结果保持干净。当你只需要分组用于分支或量化但不需要提取匹配文本时就用它。(http|https):// 创建一个你可能不需要的捕获;(?:http|https):// 不会。

匹配 Unicode 字符的正确方式是什么?

在 JavaScript 中,加上 u 标志并使用 Unicode property escapes:/\p{Letter}+/gu 匹配任意书写系统的字母序列。没有 u 标志,\w 只匹配 ASCII 词字符。Python 的 re 模块在 Python 3 中默认是 Unicode 感知的。Java 需要 Pattern.UNICODE_CHARACTER_CLASS。大多数引擎都有某种方式来支持 Unicode;查你那个引擎的文档。

相关工具

免费在线 JSON 格式化器和验证器 免费 URL 编码器/解码器 大小写转换器