语法、规则与示例

正则表达式速查表

所有正则规则都在这一页,每条都附有可以直接上手的可交互示例。写给 Regex Hunter 的玩家,也写给所有编写正则的人。

字符与转义

详情 →
语法含义 · 示例
abc按顺序排列的字符 a、b、c
.除换行符以外的任意字符
\. \* \\按字面匹配的元字符
\t \n \r制表符、换行符、回车符

字符类与简写

详情 →
语法含义 · 示例
[abc]a、b、c 中的一个
[^abc]除 a、b、c 以外的任意字符
[a-z]范围:任意小写字母
\d \D数字 / 非数字
\w \W单词字符 [A-Za-z0-9_] / 非单词字符
\s \S空白字符 / 非空白字符
\p{L}任何文字中的任意字母(需加 u 修饰符)
语法含义 · 示例
^文本开头(用 m 时为行首)
$文本结尾(用 m 时为行尾)
\b单词边界
\B非单词边界
语法含义 · 示例
*零次或多次
+一次或多次
?可选:零次或一次
{3}恰好 3 次
{2,5}2 到 5 次
{2,}2 次或更多
*? +? ??懒惰:尽可能少

分组与反向引用

详情 →
语法含义 · 示例
(abc)捕获组
(?:abc)非捕获组
(?<name>abc)命名捕获组
\1第 1 组捕获的文本
\k<name>命名分组捕获的文本
a|ba 或 b
语法含义 · 示例
(?=abc)后面紧跟 abc(先行断言)
(?!abc)后面不跟 abc
(?<=abc)前面紧挨 abc(后行断言)
(?<!abc)前面不是 abc

修饰符

详情 →
语法含义 · 示例
i忽略大小写
g全局:所有匹配,而不只是第一个
m多行:^ 和 $ 作用于每一行
sdotall:. 也匹配换行符
uUnicode:\p{…} 和 \u{…}
y粘性:只在 lastIndex 处匹配

规则与示例

01 正则表达式如何工作

正则表达式(regex)是一种用来描述文本的小型语言。引擎带着你的正则和输入文本从左到右一起前进,尝试让正则的每一部分都对上字符串中的字符。如果能对上,这个字符串就匹配了。

几乎所有操作都围绕两个问题:

逐个字符匹配

像 cat 这样的正则表示“一个 c,接着一个 a,再接着一个 t,紧挨在一起”。

重复几次

像 + 和 * 这样的量词,规定前一个元素可以重复多少次。

默认情况下,只要在字符串的任意位置能匹配上,正则就算匹配——除非你用锚点把它固定住,否则它不必覆盖整个字符串。

//
✓cat
✓a cat sat
✓category
✕dog
修改正则,匹配结果会实时高亮 在沙盒中打开

在游戏中(JavaScript)

在 Regex Hunter 中,你写的是正则的主体,也就是两条斜杠之间的部分。只要你的正则在某个词的任意位置匹配,棋盘就会把这个词标记为已抓住(想要严格,就用锚点)。

02 字面字符

大多数字符都匹配它们自己。字母、数字和空格都是字面字符:regex 匹配文本“regex”,42 匹配“42”。匹配默认区分大小写,所以 Cat 不匹配“cat”(i 修饰符可以改变这一点)。

//
✓code
✕CODE
✓decode
✓coder
修改正则,匹配结果会实时高亮 在沙盒中打开

练习学院: 狩猎入门

03 转义元字符

元字符是正则中具有特殊含义的十几个字符:

. ^ $ * + ? ( ) [ ] { } | \\ /

想按字面匹配其中一个,就在它前面加一个反斜杠。\. 匹配真正的点号,\+ 匹配真正的加号,\\ 匹配真正的反斜杠。在字符类里,这些字符大多失去特殊作用,不需要转义。

//
✓3.14
✕3x14
✕3,14
修改正则,匹配结果会实时高亮 在沙盒中打开

练习学院: 狩猎入门 · 去玩: 转义狩猎

04 点号 ( . )

点号 . 匹配除换行符以外的任意单个字符(除非开启了 s / dotall 修饰符)。它是正则里最常被滥用的符号——当你真正想表达“任意字母”或“任意数字”时,用字符类更稳妥。

//
✓cat
✓cot
✓c4t
✕ct
✕caat
修改正则,匹配结果会实时高亮 在沙盒中打开

这里 c.t 要求 c 和 t 之间恰好有一个字符,所以“ct”(零个)和“caat”(两个)都不匹配。

练习学院: 狩猎入门

05 字符类 [ ]

方括号定义一个集合:恰好匹配其中的一个字符。[aeiou] 匹配任意一个元音字母。用连字符表示范围:[a-z]、[0-9]、[A-Fa-f0-9](一个十六进制数字)。以 ^ 开头表示取反:[^0-9] 就是“任何不是数字的字符”。

语法含义
[abc]a、b 或 c 中的一个
[a-z]任意小写字母
[0-9a-fA-F]一个十六进制数字
[^aeiou]除小写元音以外的任意字符
[.\-]字面的点号或连字符(在字符类里,大多数元字符都按字面意义匹配)
//
✓C
✓F
✕H
✕c
✕7
修改正则,匹配结果会实时高亮 在沙盒中打开

易错点

连字符放在开头或结尾时是字面字符([-a]、[a-])。要包含 ],就转义它([\]a]):在 JavaScript 中 [] 是空字符类,所以 []a] 不起作用。^ 只有作为第一个字符时才表示取反。

练习学院: 字符类 · 练习包: 字符城堡 · 去玩: 字符类狩猎

06 简写字符类

简写字符类是常用字符集的单字母简写,比如 \d 表示任意数字。大写字母表示对应小写字母的取反。

语法含义
\d \D一个数字 [0-9] / 非数字
\w \W一个单词字符 [A-Za-z0-9_] / 非单词字符
\s \S空白字符(空格、制表符、换行符……)/ 非空白字符
\t \n \r制表符、换行符、回车符
//
✓order 66
✕no digits here
✓2026
✓a1b2
修改正则,匹配结果会实时高亮 在沙盒中打开

在游戏中(JavaScript)

在 JavaScript 中,\d 和 \w 只匹配 ASCII,即使加上 u 修饰符也一样;其他文字请用 \p{L} 或 \p{N}。\s 本来就能匹配 Unicode 空白。在正则高尔夫里,它们比完整写出一个字符类短得多。

练习学院: 字符类

07 锚点与边界

锚点匹配的是位置,而不是字符——它们的宽度为零。

语法含义
^字符串的开头(使用 m 修饰符时为行首)
$字符串的结尾(使用 m 修饰符时为行尾)
\b单词边界——\w 与非 \w 之间的交界
\B不是单词边界的位置

用 ^…$ 包住正则,就能强制它匹配整个字符串。\bcat\b 匹配完整的单词“cat”,但不匹配“category”或“scatter”。

//
✓cat
✓a cat sat
✕category
✕bobcat
✕concatenate
修改正则,匹配结果会实时高亮 在沙盒中打开

练习学院: 锚点与边界 · 练习包: 抛锚定位 · 去玩: 锚点狩猎

08 量词

量词规定前一个元素可以重复多少次。

语法含义
*零次或多次
+一次或多次
?零次或一次(可选)
{3}恰好 3 次
{2,5}2 到 5 次
{2,}2 次或更多

量词作用于紧挨在它前面的东西——一个字符、一个字符类或一个分组。colou?r 让 u 变成可选(匹配“color”和“colour”);(ab)+ 重复这一对字符。

//
✕a
✓aa
✓aaaa
✕aaaaa
修改正则,匹配结果会实时高亮 在沙盒中打开

练习学院: 量词 · 练习包: 量词矿场 · 去玩: 量词狩猎

09 贪婪、懒惰与占有

默认情况下,量词是贪婪的:先尽可能多地匹配,如果正则的其余部分需要,再把字符退回去。加上 ? 就能让它变成懒惰的——尽可能少地匹配。

经典例子:对于 <a><b>,贪婪的 <.*> 会匹配整串内容,而懒惰的 <.*?> 在第一个 > 处就停下。

//g
✓<a><b>
✓<one>two<three>
修改正则,匹配结果会实时高亮 在沙盒中打开

占有量词与原子组(JavaScript 不支持)

PCRE/Java 还提供占有量词(a*+)和原子组((?>…)),它们从不退回字符,可以防止灾难性回溯。JavaScript 两者都没有;可以用先行断言加反向引用来模拟原子组。

练习学院: 量词

10 分支与分组

| 表示“或”,优先级最低——除非用括号把它包起来,否则它会把整个正则一分为二。cat|dog 匹配两个单词中的任意一个;gr(a|e)y 匹配“gray”或“grey”,而 gra|ey 的意思则是“gra”或“ey”。

//
✓gray
✓grey
✕groy
修改正则,匹配结果会实时高亮 在沙盒中打开

练习学院: 分组与反向引用 · 去玩: 分支狩猎

11 分组:捕获与命名

括号有两个作用:把多个元素分组,让量词或 | 作用于整组;同时捕获匹配到的内容,供之后再用。

语法含义
(…)捕获组——从左到右编号为 1、2、3……
(?:…)非捕获组——只分组,不记录
(?<name>…)命名捕获组

只需要分组时就用 (?:…)(更快,也能让分组编号保持整齐);当一个正则有好几个值得标注的捕获时,就给它们起名字。

//
✓ab
✓abab
✓ababab
✓aba
修改正则,匹配结果会实时高亮 在沙盒中打开

练习学院: 分组与反向引用 · 练习包: 刁钻正则 · 去玩: 分组狩猎

12 反向引用

反向引用匹配的是某个分组已经捕获的同一段文本。\1 指第 1 组,\2 指第 2 组,\k<name> 指一个命名分组。要求内容重复,就靠它:

语法含义
(.)\1任意字符后面紧跟它自己——双写字母
(.)(.)\2\14 个字符的回文(例如“abba”)
(.).*\1同一个字符在后面再次出现
//
✓book
✓tree
✕cat
✓success
修改正则,匹配结果会实时高亮 在沙盒中打开

反向引用 vs. 字符类

关键在于:(.)\1 要求同一个字符出现两次——这和 [a-z][a-z](任意两个字母)不同。正是反向引用让正则能够检查“同样的东西又出现了一次”,这是普通字符类表达不了的。

练习学院: 分组与反向引用

13 先行断言与后行断言

环视用来断言当前位置旁边是否出现某些内容,但不消耗任何字符——它是一个零宽度的条件。

语法含义
(?=…)正向先行断言——后面紧跟 …
(?!…)负向先行断言——后面不跟 …
(?<=…)正向后行断言——前面紧挨 …
(?<!…)负向后行断言——前面不是 …

像“包含一个数字和一个字母”这样的密码规则,会在开头叠加多个先行断言:^(?=.*\d)(?=.*[a-z]).+$。每个 (?=…) 都从位置 0 开始检查整个字符串,然后匹配继续进行。在大多数引擎中,后行断言要求固定(或有上限的)宽度。

//g
✓16px
✓24 px
✕16pt
✕px
修改正则,匹配结果会实时高亮 在沙盒中打开

在游戏中(JavaScript)

JavaScript 从 ES2018 起支持后行断言——现代浏览器都能正常运行,所以在游戏里可以用。较旧的引擎(以及一些工具)不支持。

练习学院: 环视 · 去玩: 环视狩猎

14 修饰符

修饰符是改变整个正则匹配方式的单个字母,比如 i 表示不区分大小写。在代码里,它们写在结尾斜杠之后(/cat/gi)。

语法含义
i忽略大小写
g全局——找出所有匹配,而不只是第一个
m多行——^ 和 $ 在每个换行处都能匹配
sdotall——. 也匹配换行符
uUnicode 模式(启用 \u{…} 和 \p{…})
y粘性——只在 lastIndex 处匹配
x扩展/详细模式:忽略空白并允许注释(PCRE/Python 支持;JS 不支持)
//gi
✓Cat caught a CAT
✓category
修改正则,匹配结果会实时高亮 在沙盒中打开

15 Unicode 与属性

加上 u 修饰符,就可以按 Unicode 属性匹配:\p{L} 是任何文字中的任意字母,\p{N} 是任意数字,\p{Emoji} 是表情符号,\P{…} 表示取反。\u{1F600} 按十六进制值匹配一个码点。

不加 u 时,正则按 UTF-16 码元工作,所以一个表情符号或其他辅助平面字符会被算作两个“字符”——这通常不是你想要的。属性转义才是处理真实文本的干净办法。

引擎支持

JavaScript(需加 u)、PCRE、Python(通过 regex 模块)、Java 等都支持属性转义,但具体的属性名称各不相同。为了可移植性,请使用常见的那些(L、N、P、文字名称)。

16 常用正则

这些是适用于日常格式的简短、可复用正则:整数、小数、邮箱、十六进制颜色和 ISO 日期。把“校验”类正则当作近似方案——真正的邮箱和 URL 规则远比一行正则复杂。

语法含义
^\d+$整个字符串是一个非负整数
-?\d+(\.\d+)?可带负号的小数
^[\w.+-]+@[\w-]+\.[\w.-]+$大致的邮箱格式
^#?[0-9a-fA-F]{6}$6 位十六进制颜色值
^\d{4}-\d{2}-\d{2}$ISO 日期(YYYY-MM-DD)
//
✓42
✓-3.14
✕3.
✕x9
修改正则,匹配结果会实时高亮 在沙盒中打开

练习学院: 实战校验 · 去玩: 综合狩猎

17 写出更短正则的技巧

正则高尔夫的艺术,就是写出最短的正则,抓住每个目标、放过每个要放过的词。棋盘只问“它在任意位置匹配吗”——所以你很少需要锚点,也很少需要描述整个单词。

  • 找出最小的区别特征。如果每个目标都包含 q,而要放过的词都不包含,答案就是 q。
  • 优先用字符类代替分支:[cb]at 胜过 cat|bat。
  • 简写很短:用 \d 代替 [0-9],用 \w 代替 [A-Za-z0-9_]。
  • 除非某个要放过的词逼你加,否则别用锚点。只有在需要排除一个几乎相同的词时,才加上 ^ 或 $。
  • 用 ? 让差异变成可选,而不是开分支。
  • 当目标共有一种要放过的词所没有的重复时,就用反向引用。

试着把它改短——cat 和 bat 都是目标,“rat”要放过:

//
✓cat
✓bat
✕rat
✕mat
修改正则,匹配结果会实时高亮 在沙盒中打开

准备好一较高下了吗?每日狩猎、练习包和沙盒都是实战的好地方。

练习学院: 高尔夫技巧

18 各引擎的差异

正则方言是不同正则引擎各自的语法变体,它们支持的功能并不完全相同。主要的几大家族是 JavaScript、PCRE(PHP,与 Perl 接近)、Python(re / regex)和 Java。

语法含义
后行断言JS(ES2018+)、PCRE、Python、Java——大多要求固定宽度;PCRE/Python 更宽松
占有 / 原子PCRE、Java、Python regex——JavaScript 不支持
递归 (?R)仅 PCRE 和 Python regex——JS 和 Python re 都不支持
命名分组JS/PCRE/.NET 用 (?<n>);传统 Python 用 (?P<n>)
x / 详细模式修饰符PCRE、Python、Java——JavaScript 不支持

在游戏中(JavaScript)

游戏使用浏览器的 JavaScript 引擎来执行你的正则,所以遵循 JS 的规则:后行断言和命名分组可以用;占有量词和递归不行。上方的可交互示例使用的也是同一个引擎。

19 性能与回溯

大多数正则都很快,但对同一批字符嵌套使用量词可能会爆炸。(a+)+$ 面对一长串“a”再加一个“!”时,会迫使引擎尝试指数级数量的拆分方式——这就是灾难性回溯。

  • 避免在量词里再套一个能匹配同样内容的量词((a+)+、(.*)*)。
  • 让内部各部分互斥,这样就只有一种匹配方式。
  • 用 ^/$ 锚定,并优先使用具体的字符类而不是 .,以缩小搜索空间。
  • 在支持它们的引擎中,占有量词 / 原子组可以彻底阻止回溯。