正则表达式用普通字符与元字符组合定义字符串匹配规则,是文本搜索、替换、验证的通用工具。语法跨语言高度一致,本文介绍来源和基础语法;
Python 标准库的 API 用法单独成篇,见 Python re 模块。
正则表达式(Regular Expression),中文叫;口语简写还有 regex / regexp。Python 的标准库直接取名 re。
这里的**正则(regular)来自自动机理论里的 ** 正则集(regular set),不是“合乎规则”这种日常中文理解。
-
1956 数学家 Stephen Kleene 提出正则代数,用来描述一类有限自动机能识别的语言集合,这种集合就叫 regular set,描述它的式子叫 regular expression。
regular 在数学里含义:可由有限状态自动机描述的集合,翻译时译作「正则」。
-
后续 Ken Thompson(Unix之父)把这套数学理论搬到 Unix 的文本编辑器
ed,诞生了g/re/p命令(global / regular expression / print,也就是后来的grep),正则正式进入计算机世界。
容易踩坑: 中文名字里的正则是数学术语翻译,不要理解成“正规、正确的表达式”。它特指正则语言对应的表达式。
元字符速查
| 元字符 | 含义 | 示例 |
|---|---|---|
. | 任意单字符(默认不含换行) | a.c → abc, a1c |
^ | 字符串开头 | ^The |
$ | 字符串结尾 | end$ |
* | 0次或多次 | ab* → a, ab, abbb |
+ | 1次或多次 | ab+ → ab, abbb |
? | 0次或1次 | ab? → a, ab |
{n} | 精确n次 | a{3} → aaa |
{n,m} | n到m次 | a{2,4} |
| | 或 | cat|dog → cat, dog |
[] | 字符集 | [aeiou], [0-9] |
() | 分组捕获 | (abc)+ |
转义序列与元字符配合使用,出现频率同样高:
| 转义 | 含义 | 等价写法 |
|---|---|---|
\d / \D | 数字 / 非数字 | [0-9] / [^0-9] |
\w / \W | 单词字符 / 非单词字符 | [a-zA-Z0-9_] |
\s / \S | 空白 / 非空白 | [ \t\n\r\f\v] |
\b | 单词边界 | — |
贪婪与惰性
用来规定前面那个模式,要匹配多少次的词,称为”量词“。它跟在一个字符/字符组/分组后面,控制重复次数。
| 量词 | 含义 |
|---|---|
* | 0次或多次(≥0) |
+ | 1次或多次(≥1) |
? | 0次或1次(最多1次) |
{n} | 恰好 n 次 |
{n,} | 至少 n 次 |
{n,m} | n ~ m 次 |
量词默认贪婪:尽可能多匹配,再回溯让出字符;量词后加 ? 变惰性,只取满足条件的最短匹配。提取 HTML 标签是经典对照:
import re
re.findall(r'<.*>', '<a>hi</a><b>yo</b>') # ['<a>hi</a><b>yo</b>'] 贪婪,整行吞掉
re.findall(r'<.*?>', '<a>hi</a><b>yo</b>') # ['<a>', '</a>', '<b>', '</b>'] 惰性,逐个标签.+量词+贪婪- 引擎:从
<开始,.一口气吞到字符串末尾 - 发现末尾没有
>,回溯,吐出字符,直到找到第一个> - 匹配结果:
<a> <b>(一次性抓到最后那个>)
Note
量词 = 控制前面的规则重复多少次;默认贪心拿最多,加问号改成拿最少。
常用模式
# 邮箱
^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$
# 中国手机号
^1[3-9]\d{9}$
# 日期 YYYY-MM-DD
^\d{4}-\d{2}-\d{2}$
# URL
^(https?://)?(www\.)?[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}(/.*)?$高级特性
| 特性 | 语法 | 用途 |
|---|---|---|
| 非捕获分组 | (?:abc) | 仅匹配,不保存 |
| 命名分组 | (?P<name>abc) | 按名引用捕获内容 |
| 反向引用 | (\w)\1 | 复用先前分组(匹配叠字等) |
| 正向前瞻 | (?=abc) | 后面是 abc |
| 负向前瞻 | (?!abc) | 后面不是 abc |
| 正向后瞻 | (?<=abc) | 前面是 abc |
| 负向后瞻 | (?<!abc) | 前面不是 abc |
前瞻后瞻是零宽断言:只判断位置、不消耗字符,因此同一位置可叠加多个条件。密码强度校验是典型应用——同时要求含数字、小写、大写:
(?=.*\d)(?=.*[a-z])(?=.*[A-Z])\S{8,}各语言实现
正则语法存在方言差异(PCRE、POSIX、JavaScript 各有出入),元字符语义大体一致但细节不同。Python re 模块的完整用法——函数选择、命名分组取值、re.sub 函数替换、flags 与编译复用——见 Python re 模块。
现代 Perl、Python 的正则已经超出原始 Kleene 的正则语言范围(增加反向引用、环视等,能力升级到上下文无关语法),只是名字沿用历史,依旧叫 Regular Expression。