字符类(Character Class)
字符类用方括号 [...] 定义,匹配括号内的任意一个字符。字符类相当于把多个选项压缩成一个元字符位置。
字符类(Character Class)
用
[...] 定义的一组字符,引擎在该位置匹配括号内的任意一个字符。与 | 的区别:字符类只匹配单个字符,而 | 可以匹配多字符的模式。[abc] 等价于 a|b|c,但更简洁高效。范围(Range)
在字符类中用
- 表示字符范围:[a-z] 匹配所有小写字母,[0-9] 匹配所有数字。范围基于字符的 ASCII/Unicode 码点顺序,因此 [A-z] 会包含 [ \ ] ^ _ ` 等意外字符——这是常见的误用。取反字符类(Negated Character Class)
在
[ 后面紧接 ^,表示"不是这些字符中的任何一个"。[^aeiou] 匹配任何非元音字母的单个字符(包括数字、标点、空格等)。/[aeiou]/g
hello world
字符类 [aeiou] 匹配任意一个元音字母
# 基本字符类 re.findall(r'[aeiou]', 'hello world') # → ['e', 'o', 'o'] # 字符范围 re.findall(r'[a-z]+', 'Hello World 123') # → ['ello', 'orld'] (只匹配小写字母) re.findall(r'[a-zA-Z]+', 'Hello World 123') # → ['Hello', 'World'] (大小写字母都匹配) # 数字范围 re.findall(r'[0-9]+', 'abc 123 def 456') # → ['123', '456'] # 取反字符类 re.findall(r'[^aeiou\s]+', 'hello world') # → ['h', 'll', 'w', 'rld'] (非元音且非空格的字符序列)
字符类内的特殊规则
在字符类内部,大多数元字符失去其特殊含义,但有例外:
| 字符 | 在字符类内的行为 | 说明 |
|---|---|---|
] | 结束字符类(需转义为 \]) | 放在最开头 []] 可以匹配字面 ] |
- | 表示范围(在首尾或转义后为字面 -) | [-az] 或 [az-] 或 [a\-z] |
^ | 紧接 [ 时表示取反 | 其他位置是字面 ^ |
\ | 转义(仍然有效) | [\d] 匹配数字 |
. | 字面点号(不需要转义) | [.?!] 匹配 . ? ! |
* + ? | 字面量词字符(不需要转义) | [*+?] 匹配这三个字符 |
# 在字符类中匹配字面特殊字符 re.findall(r'[.!?]', 'Hello! How are you?') # → ['!', '?'] (. ! ? 在字符类内都是字面字符) # - 放在末尾,避免被解析为范围 re.findall(r'[a-z0-9-]+', 'hello-world_2026') # → ['hello-world', '2026'] (- 在末尾,是字面连字符) # 错误示例:[A-z] 包含了意外字符 import string # [A-z] 的 ASCII 范围是 65-122,包含 [ \ ] ^ _ ` # 应该使用 [A-Za-z] 而不是 [A-z]
WARNING([A-z] 陷阱)不要使用
[A-z]!ASCII 中 Z(90)和 a(97)之间有 6 个字符:[ \ ] ^ _ `。正确写法是 [A-Za-z] 或使用预定义类 [a-zA-Z]。同理,[0-Z] 也包含了大量意外的标点符号。
预定义字符类(Predefined Character Classes)
常用字符集有简短的预定义写法,避免每次手写范围:
\d — 数字等价于
[0-9](ASCII 模式)。在 Unicode 模式下匹配更广泛的数字字符(如阿拉伯数字、全角数字)。\D 是其反义,匹配非数字字符,等价于 [^0-9]。\w — 单词字符等价于
[a-zA-Z0-9_](ASCII 模式):字母、数字和下划线。在 Unicode 模式下也匹配其他语言的字母。\W 是其反义,匹配非单词字符(标点、空格等)。\s — 空白字符匹配空格
、制表符 \t、换行 \n、回车 \r、换页 \f、垂直制表符 \v。\S 是其反义,匹配非空白字符。| 预定义类 | 等价写法(ASCII 模式) | 含义 |
|---|---|---|
\d | [0-9] | 数字 0-9 |
\D | [^0-9] | 非数字 |
\w | [a-zA-Z0-9_] | 单词字符(字母+数字+下划线) |
\W | [^a-zA-Z0-9_] | 非单词字符 |
\s | [ \t\n\r\f\v] | 空白字符 |
\S | [^ \t\n\r\f\v] | 非空白字符 |
/\w+@\w+\.\w+/g
Contact: user@example.com or admin@test.org
\w+ 匹配一个或多个单词字符,简化了邮箱的基本结构
# \d 匹配数字 re.findall(r'\d+', 'a1b22c333') # → ['1', '22', '333'] # \w 匹配单词字符 re.findall(r'\w+', 'hello, world! 123') # → ['hello', 'world', '123'] # \s 匹配空白(常用于分割) re.split(r'\s+', ' hello world ') # → ['', 'hello', 'world', ''] # \S 匹配非空白(提取不含空格的词) re.findall(r'\S+', 'hello world 2026') # → ['hello', 'world', '2026']
在字符类中使用预定义类
预定义类可以嵌入字符类中,与其他字符组合:
# 字母或数字(\w 的子集,不包含下划线) re.findall(r'[a-zA-Z\d]+', 'hello_world 123') # → ['hello', 'world', '123'] (下划线被排除) # 空白字符或逗号(用于分割 CSV 风格数据) re.split(r'[\s,]+', 'a, b,c d') # → ['a', 'b', 'c', 'd'] # 非数字非空白(提取文字部分) re.findall(r'[^\d\s]+', 'abc 123 def 456') # → ['abc', 'def']
特殊转义序列
除了 \d \w \s,正则还支持其他重要的转义序列:
| 转义序列 | 含义 | 说明 |
|---|---|---|
\n | 换行符(LF) | Unix/Linux 行尾 |
\r | 回车符(CR) | Windows 行尾的第一个字符(\r\n) |
\t | 制表符 | Tab 键 |
\0 | 空字符(NUL) | 二进制数据处理 |
\xHH | 十六进制 ASCII 字符 | \x41 = 'A' |
\uHHHH | Unicode 字符 | \u4e2d = '中'(JS 语法) |
\N{name} | Unicode 命名字符 | Python: \N{SNOWMAN} = '☃' |
单词边界(\b)的工作原理
\b 是一个零宽断言——它匹配一个位置,不消耗字符。理解它的工作原理有助于正确使用:
\b 的精确定义
\b 匹配 \w 与 \W 之间的位置(或字符串开头/结尾与 \w 之间的位置)。具体来说:左边是单词字符(\w)且右边是非单词字符(\W),或者相反。字符串的开头和结尾被视为非单词字符。# \b 精准匹配完整单词 re.findall(r'\bcat\b', 'cat cats catch concatenate') # → ['cat'] (只有 'cat' 是完整单词) re.findall(r'\bcat\w*', 'cat cats catch concatenate') # → ['cat', 'cats', 'catch'] (以 cat 开头的单词) # \b 的工作原理示意 # 字符串:" cat " # 位置: 0 1 2 3 4 # 字符: ' ' 'c' 'a' 't' ' ' # \b 在位置 1(空格→c,\W→\W... 等等) # 实际上 \b 在 pos1 (空格与c之间) 和 pos4 (t与空格之间) # 常见陷阱:\b 对连字符、下划线不生效 re.findall(r'\bcat\b', 'e-cat') # → ['cat'] (- 是 \W,所以 e-cat 中 cat 前有 \b) re.findall(r'\bcat\b', '_cat') # → [] (_ 是 \w,所以 _cat 中 cat 前没有 \b)
INFO(\b 与中文)在处理中文文本时,
\b 的表现可能出乎意料。中文汉字不属于 \w(ASCII 模式下),因此汉字与汉字之间的位置会被 \b 匹配,导致意外行为。处理中文时,一般不依赖 \b,而是使用中文分词工具或显式的边界模式。
Unicode 属性转义(ES2018 / Python regex 库)
对于需要匹配任意语言文字的场景,标准的 \w 不够用——它在 ASCII 模式下只匹配英文字母。Unicode 属性转义提供了更精确的控制:
// JavaScript(ES2018+),需要 u 标志 /\p{Letter}+/u.test('Héllo') // true(包含重音字母) /\p{Script=Han}/u.test('汉字') // true(汉字) /\p{Script=Hiragana}/u.test('あ') // true(平假名) /\p{Decimal_Number}/u.test('2') // true(全角数字) # Python:标准 re 模块对 Unicode 的支持有限 # 使用第三方 regex 库获得完整的 Unicode 属性支持 # pip install regex import regex regex.findall(r'\p{Han}+', 'hello 你好 world 世界') # → ['你好', '世界']
POSIX 字符类(在 PCRE 中可用)
在 ERE(扩展正则表达式)和 PCRE 中,[:name:] 形式的 POSIX 字符类在方括号内使用:
| POSIX 类 | 等价写法 | 含义 |
|---|---|---|
[:alpha:] | [a-zA-Z] | 字母 |
[:digit:] | [0-9] | 数字 |
[:alnum:] | [a-zA-Z0-9] | 字母或数字 |
[:lower:] | [a-z] | 小写字母 |
[:upper:] | [A-Z] | 大写字母 |
[:space:] | [ \t\n\r\f\v] | 空白字符 |
[:punct:] | 标点符号集 | 标点符号 |
INFO(POSIX 类在 Python/JS 中的支持)Python 的
re 模块不支持 POSIX 字符类。JavaScript 也不支持。这种语法仅在 grep、awk、sed、PostgreSQL 等工具中有效。在 Python/JS 中使用对应的 \d、\w、\s 或明确的范围 [a-zA-Z]。
常见字符类模式总结
# 验证:仅包含字母和数字 re.fullmatch(r'[a-zA-Z0-9]+', username) # 提取:十六进制数字(如颜色值) re.findall(r'#[0-9a-fA-F]{6}', 'color: #ff5733 and #abc123') # → ['#ff5733', '#abc123'] # 提取:标点符号 re.findall(r'[^\w\s]', 'hello, world! it\'s fine.') # → [',', '!', "'", '.'] # 提取:非 ASCII 字符(中文、日文等) re.findall(r'[^\x00-\x7F]+', 'hello 世界 world') # → ['世界'] (\x00-\x7F 是 ASCII 范围) # 匹配:文件扩展名(图片文件) re.findall(r'\.\w+$', 'photo.jpg', re.IGNORECASE) # → ['.jpg'] # 验证:密码包含大写、小写、数字 # 用三个独立的 fullmatch 检查更清晰: def has_upper(s): return bool(re.search(r'[A-Z]', s)) def has_lower(s): return bool(re.search(r'[a-z]', s)) def has_digit(s): return bool(re.search(r'\d', s))
小结
本章要点
- 字符类
[...]匹配括号内的任意一个字符;[^...]匹配不在括号内的任意字符 - 范围
a-z基于码点顺序;不要用[A-z](包含意外字符),应用[A-Za-z] - 字符类内部,
.*+?是字面字符;]需转义;-放在首尾或转义为字面连字符 - 预定义类:
\d(数字)、\w(单词字符)、\s(空白)及其大写反义\D \W \S \b单词边界匹配\w与\W之间的位置;对中文无效- Unicode 属性转义:JavaScript 的
\p{Letter}(需u标志),Python 需用第三方regex库