正则表达式
正则表达式(Regular Expression)是一套描述字符串匹配规则的语法,常用于表单校验、爬虫数据提取、日志分析等场景。Python 的 re 模块提供完整支持。
元字符速查
预定义字符集
| 元字符 | 匹配内容 |
|---|---|
. | 任意字符(不含换行符 \n,加 re.S 后包含) |
\d | 数字 [0-9] |
\D | 非数字 |
\w | 字母、数字、下划线 [a-zA-Z0-9_](支持 Unicode) |
\W | 非 \w |
\s | 空白符(空格、\t、\n、\r) |
\S | 非空白符 |
\n | 换行符 |
\t | 制表符 |
[abc] | 字符组,匹配其中任一字符 |
[^abc] | 反字符组,匹配不在其中的字符 |
[a-z] | 范围,匹配 a 到 z 的任一小写字母 |
量词
| 量词 | 含义 |
|---|---|
? | 0 次或 1 次 |
+ | 1 次或多次 |
* | 0 次或多次 |
{n} | 恰好 n 次 |
{n,} | 至少 n 次 |
{n,m} | n 到 m 次 |
量词默认贪婪匹配(尽可能多匹配),在量词后加 ? 变为非贪婪(尽可能少匹配)。
边界与分组
| 语法 | 含义 |
|---|---|
^ | 字符串开头(多行模式下为每行开头) |
$ | 字符串结尾(多行模式下为每行结尾) |
\b | 单词边界 |
a|b | 匹配 a 或 b |
(abc) | 分组,可通过 group(n) 提取 |
(?:abc) | 非捕获分组,不占用编号 |
(?P<name>abc) | 命名分组 |
(?P=name) | 引用命名分组已匹配的内容 |
re 模块常用函数
import re
text = "2024-06-01, 联系电话: 13812345678, 备用: 010-87654321"
# findall:返回所有匹配的列表
phones = re.findall(r"1[3-9]\d{9}", text)
print(phones) # ['13812345678']
# search:返回第一个匹配对象(未匹配返回 None)
m = re.search(r"(\d{4})-(\d{2})-(\d{2})", text)
if m:
print(m.group()) # 2024-06-01(整个匹配)
print(m.group(1)) # 2024(第一个分组)
print(m.group(2)) # 06
print(m.span()) # (0, 10)(匹配的起止位置)
# match:只匹配字符串开头(其余与 search 相同)
m = re.match(r"\d{4}", text) # 匹配开头是否是4位数字
# fullmatch:整个字符串必须完全匹配
m = re.fullmatch(r"\d{11}", "13812345678") # 完整11位数字
# finditer:返回迭代器,每个元素是 Match 对象
for m in re.finditer(r"\d+", text):
print(m.group(), m.start())
# sub:替换匹配内容
clean = re.sub(r"\d{11}", "***", text)
print(clean) # 2024-06-01, 联系电话: ***, 备用: 010-87654321
# subn:替换并返回替换次数
result, count = re.subn(r"\d+", "N", text)
print(result, count)
# split:按匹配分割字符串
parts = re.split(r"[,\s]+", "a, b, c,d")
print(parts) # ['a', 'b', 'c', 'd']compile — 预编译正则
当同一模式需要多次使用时,预编译可提高性能:
import re
# 预编译
phone_pat = re.compile(r"1[3-9]\d{9}")
date_pat = re.compile(r"(\d{4})-(\d{2})-(\d{2})")
# 编译后的对象与 re 函数用法相同
print(phone_pat.findall("手机: 13912345678, 座机: 010-88888888"))
m = date_pat.search("日期: 2024-06-01")
if m:
year, month, day = m.groups()
print(year, month, day) # 2024 06 01修饰符(标志)
import re
# re.I(IGNORECASE):忽略大小写
re.findall(r"python", "Python PYTHON python", re.I)
# ['Python', 'PYTHON', 'python']
# re.M(MULTILINE):^ $ 匹配每行的开头/结尾
text = "first\nsecond\nthird"
re.findall(r"^\w+", text, re.M) # ['first', 'second', 'third']
# re.S(DOTALL):. 匹配包括换行符在内的所有字符
re.search(r"a.+b", "a\nb", re.S) # 可以匹配跨行内容
# re.X(VERBOSE):允许在正则中写注释和空格(提高可读性)
email_pat = re.compile(r"""
[\w.\-]+ # 用户名部分
@ # @ 符号
[\w.\-]+ # 域名部分
\.\w{2,6} # 顶级域名
""", re.X)贪婪 vs 非贪婪
import re
html = "<b>加粗</b>和<i>斜体</i>"
# 贪婪(默认):匹配尽可能长的字符串
re.findall(r"<.+>", html)
# ['<b>加粗</b>和<i>斜体</i>'](从第一个 < 到最后一个 >)
# 非贪婪(量词后加 ?):匹配尽可能短的字符串
re.findall(r"<.+?>", html)
# ['<b>', '</b>', '<i>', '</i>']命名分组
命名分组比编号分组更清晰,通过 (?P<name>...) 定义,m.group("name") 提取:
import re
log = "2024-06-01 14:30:00 ERROR 数据库连接失败"
pat = re.compile(
r"(?P<date>\d{4}-\d{2}-\d{2})"
r" (?P<time>\d{2}:\d{2}:\d{2})"
r" (?P<level>\w+)"
r" (?P<message>.+)"
)
m = pat.match(log)
if m:
print(m.group("date")) # 2024-06-01
print(m.group("level")) # ERROR
print(m.group("message")) # 数据库连接失败
print(m.groupdict()) # 所有命名分组的字典实用正则示例
import re
# 手机号(大陆)
re.fullmatch(r"1[3-9]\d{9}", "13812345678")
# 邮箱
re.fullmatch(r"[\w.\-]+@[\w.\-]+\.\w{2,6}", "[email protected]")
# 日期(YYYY-MM-DD)
re.fullmatch(r"[1-9]\d{3}-(0[1-9]|1[0-2])-(0[1-9]|[12]\d|3[01])", "2024-06-01")
# QQ 号(5-11 位,不以 0 开头)
re.fullmatch(r"[1-9]\d{4,10}", "123456789")
# IPv4 地址
re.fullmatch(r"(?:(?:25[0-5]|2[0-4]\d|[01]?\d\d?)\.){3}(?:25[0-5]|2[0-4]\d|[01]?\d\d?)", "192.168.1.1")
# 提取 HTML 标签内容(简单场景,复杂场景推荐用 BeautifulSoup)
html = "<h1>标题</h1><p>段落</p>"
tags = re.findall(r"<(\w+)>(.*?)</\1>", html)
# [('h1', '标题'), ('p', '段落')]正则表达式适合简单的字符串模式匹配,但不适合解析 HTML(嵌套结构)或 JSON。这类场景请使用专门的解析库(BeautifulSoup、
json 模块等)。最后更新于