一、核心前提:re 模块常用函数速查
| 函数 | 作用 | 核心参数 | 返回值 | 场景 |
|---|---|---|---|---|
re.match(pattern, string) |
从字符串开头匹配 | pattern(正则表达式)、string(目标字符串) | 匹配对象 / None | 前缀验证(如手机号、邮箱前缀) |
re.search(pattern, string) |
字符串任意位置匹配(首次) | 同上 | 匹配对象 / None | 提取任意位置的目标内容 |
re.findall(pattern, string) |
匹配所有符合规则的内容 | 同上 | 列表(含所有匹配结果) | 批量提取(如所有链接、手机号) |
re.finditer(pattern, string) |
迭代器形式返回所有匹配 | 同上 | 迭代器(含匹配对象) | 大量数据提取(省内存) |
re.sub(pattern, repl, string) |
替换匹配内容 | 新增 repl(替换字符串 / 函数) | 替换后的字符串 | 清洗数据(如去除特殊字符) |
re.compile(pattern) |
编译正则表达式 | pattern | 编译后的正则对象 | 重复使用同一正则(提效) |
关键说明:
-
匹配对象常用方法:
group()(获取匹配结果)、groups()(获取分组结果)、span()(获取匹配位置); -
修饰符:
re.I(忽略大小写)、re.S(. 匹配换行)、re.M(多行匹配),可组合使用(如re.I | re.S)。
二、正则语法核心片段
| 语法 | 说明 | 示例 | 匹配结果 |
|---|---|---|---|
^ |
字符串开头 | ^1[3-9] |
匹配以 13-19 开头的字符串 |
$ |
字符串结尾 | [0-9]$ |
匹配以数字结尾的字符串 |
. |
匹配任意字符(除换行) | a.b |
匹配 a + 任意字符 + b(如 acb、aab) |
* |
前面字符出现 0-∞ 次 | ab* |
匹配 a、ab、abb... |
+ |
前面字符出现 1-∞ 次 | ab+ |
匹配 ab、abb、abbb... |
? |
前面字符出现 0-1 次(非贪婪) | ab? |
匹配 a、ab |
{n} |
前面字符出现 n 次 | a{3} |
匹配 aaa |
{n,m} |
前面字符出现 n-m 次 | a{2,4} |
匹配 aa、aaa、aaaa |
[] |
字符集(匹配其中一个) | [a-zA-Z0-9] |
匹配字母 / 数字 |
[^] |
反向字符集 | [^0-9] |
匹配非数字字符 |
\d |
匹配数字(等价 [0-9]) | \d{3} |
匹配 123、456... |
\D |
匹配非数字 | \D+ |
匹配 abc、中文... |
\w |
匹配字母 / 数字 / 下划线 | \w+ |
匹配 username123、test_abc |
\W |
匹配非字母 / 数字 / 下划线 | \W |
匹配 @、#、空格... |
\s |
匹配空白字符(空格 / 制表符 / 换行) | \s+ |
匹配多个空白 |
\S |
匹配非空白字符 | \S+ |
匹配非空白内容 |
() |
分组(提取目标内容) | (\d{3})-(\d{4}) |
分组 1 匹配前 3 位,分组 2 匹配后 4 位 |
| ` | ` | 或逻辑 | `abc |
三、场景:判断与提取
所有场景均基于 re 模块,直接复制可用,标注「判断」「提取」明确用途。
场景 1:手机号判断与提取
- 规则:11 位数字,以 13/14/15/17/18/19 开头
python
import re
def check_phone(phone):
"""判断是否为有效手机号(返回布尔值)"""
pattern = r'^1[3-9]\d{9}$' # ^$ 严格匹配整个字符串
return bool(re.match(pattern, phone))
def extract_phones(text):
"""从文本中提取所有手机号(返回列表)"""
pattern = r'1[3-9]\d{9}' # 无需^$,匹配任意位置
return re.findall(pattern, text)
# 测试
print(check_phone("13812345678")) # True
print(extract_phones("联系电话:13987654321,备用15600001111")) # ['13987654321', '15600001111']
场景 2:邮箱判断与提取
- 规则:用户名 @域名(支持字母、数字、下划线、点号)
python
def check_email(email):
"""判断是否为有效邮箱"""
pattern = r'^[a-zA-Z0-9_-]+@[a-zA-Z0-9_-]+.[a-zA-Z]{2,6}$'
return bool(re.match(pattern, email))
def extract_emails(text):
"""提取文本中所有邮箱"""
pattern = r'[a-zA-Z0-9_-]+@[a-zA-Z0-9_-]+.[a-zA-Z]{2,6}'
return re.findall(pattern, text)
# 测试
print(check_email("test123@example.com")) # True
print(extract_emails("邮箱:user_name@qq.com,工作邮箱work.123@gmail.com")) # ['user_name@qq.com', 'work.123@gmail.com']
场景 3:提取 URL 链接
- 规则:匹配 http/https 开头的链接
python
def extract_urls(text):
pattern = r'https?://[^\s]+' # http或https开头,直到空白字符结束
return re.findall(pattern, text)
# 测试
text = "推荐网站:https://www.csdn.net,文档https://docs.python.org"
print(extract_urls(text)) # ['https://www.csdn.net', 'https://docs.python.org']
场景 4:提取身份证号(18 位)
- 规则:17 位数字 + 最后 1 位(数字 / X/x)
python
def extract_id_cards(text):
pattern = r'\d{17}[\dXx]'
return re.findall(pattern, text)
# 测试
text = "身份证:110101199001011234,备用22020219800202567X"
print(extract_id_cards(text)) # ['110101199001011234', '22020219800202567X']
场景 5:提取中文(含短语)
python
def extract_chinese(text):
pattern = r'[\u4e00-\u9fa5]+' # 中文Unicode范围
return re.findall(pattern, text)
# 测试
text = "Python正则表达式提取中文:你好,世界!123abc"
print(extract_chinese(text)) # ['正则表达式提取中文', '你好', '世界']
场景 6:提取数字(整数 + 小数)
python
def extract_numbers(text):
pattern = r'-?\d+.?\d*' # 支持负数、整数、小数
return re.findall(pattern, text)
# 测试
text = "价格:99元,折扣价89.9元,亏损-10.5元"
print(extract_numbers(text)) # ['99', '89.9', '-10.5']
场景 7:验证密码强度(判断)
- 规则:8-16 位,含字母 + 数字 + 特殊字符(@#$%^&*)
python
def check_password_strength(password):
"""判断密码是否符合强密码规则"""
# 三个条件同时满足:长度8-16、含字母、含数字、含特殊字符
pattern = r'^(?=.*[a-zA-Z])(?=.*\d)(?=.*[@#$%^&*])[a-zA-Z0-9@#$%^&*]{8,16}$'
return bool(re.match(pattern, password))
# 测试
print(check_password_strength("Test123@")) # True
print(check_password_strength("weakpassword")) # False(无数字和特殊字符)
四、高级技巧:分组提取与非贪婪匹配
1. 分组提取
例如:提取「姓名:张三,年龄:25」中的姓名和年龄
python
text = "用户信息:姓名:张三,年龄:25,性别:男"
pattern = r'姓名:([^\u4e00-\u9fa5]+),年龄:(\d+)' # 分组1匹配姓名,分组2匹配年龄
result = re.search(pattern, text)
if result:
name = result.group(1)
age = result.group(2)
print(f"姓名:{name},年龄:{age}") # 姓名:张三,年龄:25
2. 非贪婪匹配
问题:.* 默认贪婪(匹配尽可能多的内容),需用 .*? 非贪婪
例如,正则表达式 a.*b 在字符串 'a123b456b' 中,.* 会一口气吃掉从第一个 a 之后到最后一个 b 之前的所有字符,最终匹配的是 'a123b456b' 整个子串,而不是我们可能期望的 'a123b'。
非贪婪匹配:在量词后面加上 ?,量词会尽可能少地重复匹配,只要能让整个表达式匹配成功。正则表达式改为 a.*?b ,得到 'a123b'。
| 贪婪写法 | 非贪婪写法 | 说明 |
|---|---|---|
| * | *? | 零次或多次,尽可能少 |
| + | +? | 一次或多次,尽可能少 |
| ? | ?? | 零次或一次,倾向于零次 |
| {m,n} | {m,n}? | 至少 m 次,至多 n 次,尽可能少 |
python
# 提取 HTML 标签内容
html = '<div>第一段</div><div>第二段</div>'
# 贪婪匹配:.* 会匹配到最后一个 </div>
greedy_tags = re.findall(r'<div>(.*)</div>', html)
print("贪婪匹配提取内容:", greedy_tags) # 输出: ['第一段</div><div>第二段']
# 非贪婪匹配:.*? 只匹配到最近的 </div>
non_greedy_tags = re.findall(r'<div>(.*?)</div>', html)
print("非贪婪匹配提取内容:", non_greedy_tags) # 输出: ['第一段', '第二段']
在正则表达式中,? 有两种用法:
-
作为普通量词,表示"零次或一次"(贪婪)。
-
跟在其他量词后面,表示将贪婪变为非贪婪(懒惰)。
例如:
-
.*? 表示"任意字符重复零次或多次,但尽可能少"。
-
.+? 表示"任意字符重复一次或多次,但尽可能少"。
-
a?? 表示"匹配一个 a 或零个 a,但优先零个"。
python
# ?? 示例:匹配两个字符,但尽可能少
print(re.findall(r'a??', 'aa')) # 输出: ['', 'a', ''] 因为第一个位置优先匹配0个a
五、常见问题与避坑指南
- 匹配不到换行符:
问题:. 默认不匹配换行,需加修饰符 re.S
示例:re.search(r'a.b', 'a\nb', re.S) → 匹配成功
- 忽略大小写匹配:
加修饰符 re.I,示例:re.match(r'abc', 'ABC', re.I) → 匹配成功
- 正则中有特殊字符(如 . * ?):
需转义(加 \)或用 re.escape() 自动转义
示例:匹配 www.baidu.com → pattern = r'www\.baidu\.com' 或 pattern = re.escape('www.baidu.com')
- 重复使用同一正则:
用 re.compile() 编译,提升效率
示例:
python
pattern = re.compile(r'1[3-9]\d{9}')
print(pattern.match("13812345678")) # 直接使用编译后的对象
print(pattern.findall("联系电话13987654321"))
六、常用
| 需求 | 正则表达式 | Python 代码片段 |
|---|---|---|
| 判断手机号 | ^1[3-9]\d{9}$ |
re.match(r'^1[3-9]\d{9}$', phone) |
| 判断邮箱 | ^[a-zA-Z0-9_-]+@[a-zA-Z0-9_-]+.[a-zA-Z]{2,6}$ |
re.match(r'^[a-zA-Z0-9_-]+@[a-zA-Z0-9_-]+.[a-zA-Z]{2,6}$', email) |
| 提取手机号 | 1[3-9]\d{9} |
re.findall(r'1[3-9]\d{9}', text) |
| 提取邮箱 | [a-zA-Z0-9_-]+@[a-zA-Z0-9_-]+.[a-zA-Z]{2,6} |
re.findall(r'[a-zA-Z0-9_-]+@[a-zA-Z0-9_-]+.[a-zA-Z]{2,6}', text) |
| 提取中文 | [\u4e00-\u9fa5]+ |
re.findall(r'[\u4e00-\u9fa5]+', text) |
| 提取数字(含小数) | -?\d+.?\d* |
re.findall(r'-?\d+.?\d*', text) |
| 提取 URL | https?://[^\s]+ |
re.findall(r'https?://[^\s]+', text) |