正则表达式字符类:\d、\w、\s、[a-z] 及其陷阱
字符类是正则中最常用的部分,也最容易出现不易察觉的错误。本文讲解 [abc]、[a-z]、[^…]、\d、\w 和 \s 到底匹配什么,以及它们背后的陷阱:[A-z]、放错位置的连字符、方括号里的点,以及不是 0 到 9 的数字。

· 阅读约 6 分钟
/^[A-Z][a-z]+$/
抓住
- Paris
- Lima
- Oslo
- Rome
放过
- paris
- PARIS
- New York
- R2D2
字符类匹配集合中的一个字符。[aeiou] 匹配一个元音字母,[0-9] 匹配一个数字,\s 匹配一个空格、制表符或换行符。
你写的几乎每个正则里都有字符类,而它们的陷阱大多很隐蔽:正则在你试过的输入上运行正常,之后却放进了一些奇怪的东西。本文先讲每种字符类到底匹配什么,再讲陷阱。每个示例都可以实时编辑,修改正则就能看到各行的变化。
01方括号里的集合
方括号列出一个位置上允许出现的字符:
一个字符类里可以放多个范围和单个字符:[A-Za-z0-9_] 表示字母、数字和下划线。
03否定字符类
紧跟在左方括号后面的 ^ 会反转集合:“除这些以外的任意字符”。
注意,“除这些以外的任意字符”真的是任意字符:空格、数字、标点和换行符都算。2024 能匹配,因为数字不是元音字母。
在方括号外,^ 是锚点。在方括号内的最开头,它表示否定。在方括号内的其他位置,它就只是一个脱字符。
04简写
有些字符类太常用了,所以有简短的写法:
| 简写 | 含义(JavaScript 中) | 反义 |
|---|---|---|
\d |
一个数字,[0-9] |
\D |
\w |
一个单词字符,[A-Za-z0-9_] |
\W |
\s |
空白字符:空格、制表符、换行符等 | \S |
. |
除换行符以外的任意字符 |
其中两个常让人意外。\w 包含数字和下划线,所以它并不等于“一个字母”:
而 \s 也不只是空格:它还匹配制表符和换行符。如果你只想要一个空格,就直接写一个空格。
05陷阱 1:[A-z] 不是“所有字母”
范围按字符编码计算。在 Z(90)和 a(97)之间还有六个其他字符:[ \ ] ^ _ 和反引号。所以 [A-z] 会把它们也放进来:
把两个范围分开写:[A-Za-z]。
06陷阱 2:中间的连字符
两个字符之间的连字符会构成范围,即使你本意只是一个普通的连字符:
那个逗号就是 bug:+ 的编码是 43,. 是 46,逗号(44)正好在两者之间。把连字符放在字符类的开头或末尾,或者转义它,它就又变回普通的连字符了:
07陷阱 3:点和其他特殊字符
大多数特殊字符在方括号内都会失去特殊含义。[.] 是字面上的点,[*+?] 是三个符号中的一个,[$] 是美元符号。所以 [.] 是 \. 的另一种写法:
在字符类内部,只有四个字符仍然有特殊含义:]、\、开头的 ^,以及两个字符之间的 -。
08陷阱 4:不是 0 到 9 的数字
在 JavaScript 中,\d 就是 [0-9]。在 Python 3 中,对于普通的 str 正则,\d 匹配任何 Unicode 数字:阿拉伯-印度数字 ٣、天城文数字 ३、全角数字 3。\w 也是如此,它接受任何文字的字母。
import re
re.fullmatch(r"\d+", "٣٤٥") # matches in Python
re.fullmatch(r"\d+", "٣٤٥", re.ASCII) # None
如果这个数字接下来要传给 int() 或存进数据库字段,这一点就很重要。想要 ASCII 数字时,请使用 [0-9] 或 re.ASCII 修饰符。
09试一试
你能抓住只由十六进制数字组成的行吗?
抓住只由十六进制数字组成的字符串,大小写均可。
目标 · 抓住 0/6
- ff目标
- 00目标
- 1a2B目标
- DEADbeef目标
- c0ffee目标
- 7目标
无辜词 · 误抓 0
- fg无辜
- 0x1f无辜
- 12 34无辜
- beef!无辜
- gg无辜
- z无辜
输入正则——棋盘会实时更新。