一、正则基础模块
Python 处理正则使用内置模块 re
python
import re
常用方法:
re.match():从开头匹配re.search():全文搜索,找到第一个匹配re.findall():返回所有匹配结果列表re.finditer():返回迭代器,适合大量数据re.sub():替换匹配内容re.split():按匹配分割字符串re.compile():预编译正则,提升效率
二、元字符(必须掌握)
1. 基础元字符
| 符号 | 含义 | |
|---|---|---|
. |
匹配任意单个字符(除换行) | |
^ |
匹配字符串开头 | |
$ |
匹配字符串结尾 | |
* |
匹配 0 次或多次(贪婪) | |
+ |
匹配 1 次或多次(贪婪) | |
? |
匹配 0 次或 1 次 | |
| ` | ` | 或 |
() |
分组 | |
[] |
字符集 | |
{n} |
精确 n 次 | |
{n,m} |
n~m 次 |
示例
python
# . 匹配任意字符
print(re.findall(r'a.b', 'a1b aab acb')) # ['a1b', 'aab', 'acb']
# ^ 开头匹配
print(re.match(r'^\d+', '123abc').group()) # 123
# $ 结尾匹配
print(re.search(r'\d+$', 'abc123').group()) # 123
# * + ?
print(re.findall(r'ab*', 'a ab abb')) # ['a', 'ab', 'abb']
print(re.findall(r'ab+', 'a ab abb')) # ['ab', 'abb']
print(re.findall(r'ab?', 'a ab abb')) # ['a', 'ab', 'ab']
三、字符类与预定义字符集
1. 自定义字符集 []
python
# 匹配 a/b/c 中的一个
print(re.findall(r'[abc]', 'a1b2c3')) # ['a','b','c']
# 范围 [0-9] [a-z] [A-Z]
print(re.findall(r'[0-9]', 'a1b2c3')) # ['1','2','3']
# 排除 ^ 在 [] 内表示非
print(re.findall(r'[^0-9]', 'a1b2c3')) # ['a','b','c']
2. 预定义字符(高频)
| 符号 | 含义 |
|---|---|
\d |
数字 [0-9] |
\D |
非数字 |
\w |
字母 / 数字 / 下划线 [a-zA-Z0-9_] |
\W |
非单词字符 |
\s |
空白(空格、\t、\n) |
\S |
非空白 |
示例:
python
print(re.findall(r'\d+', 'price:199, count:20')) # ['199','20']
print(re.findall(r'\w+', 'user_name_123')) # ['user_name_123']
四、量词与贪婪 / 非贪婪
1. 贪婪(默认)
尽可能多匹配
python
s = '<div>test</div><div>hello</div>'
print(re.findall(r'<div>.*</div>', s))
# 整条匹配:['<div>test</div><div>hello</div>']
2. 非贪婪 *? +? ??
尽可能少匹配
python
print(re.findall(r'<div>.*?</div>', s))
# ['<div>test</div>', '<div>hello</div>']
测试开发常用:提取标签、JSON 字段、日志内容。
五、分组 () 与捕获
1. 普通分组
python
res = re.search(r'(\d+)-(\d+)', 'tel:123-4567')
print(res.group(0)) # 123-4567
print(res.group(1)) # 123
print(res.group(2)) # 4567
2. 命名分组 (?P<name>)
接口提取字段非常实用
python
res = re.search(r'(?P<year>\d{4})-(?P<month>\d{2})', '2025-09')
print(res.group('year')) # 2025
print(res.group('month')) # 09
3. 非捕获分组 (?:...)
只匹配不捕获,节省内存
python
print(re.findall(r'(?:http|https)://(\w+)', 'https://www'))
# ['www']
六、断言(零宽断言,测试必用)
1. 正向先行断言 (?=...)
后面必须出现某内容
python
# 匹配数字后面跟元
print(re.findall(r'\d+(?=元)', '价格99元,数量20个'))
# ['99']
2. 负向先行断言 (?!...)
后面不出现某内容
python
print(re.findall(r'\d+(?!个)', '99元 20个'))
#['99', '2']
3. 正向后行断言 (?<=...)
前面必须出现
python
print(re.findall(r'(?<=价格)\d+', '价格199'))
# ['199']
4. 负向后行断言 (?<!...)
前面不出现
python
print(re.findall(r'(?<!价格)\d+', '原价199 价格299'))
# ['199']
七、re 常用方法示例(测试开发最实用)
1. re.match()
只匹配开头
python
print(re.match(r'hello', 'hello world').group()) # hello
2. re.search()
找第一个匹配
python
print(re.search(r'\d+', 'abc123def').group()) # 123
3. re.findall()
返回所有匹配列表(最常用)
python
print(re.findall(r'[a-z]+', '1a2b3c')) # ['a','b','c']
4. re.sub () 替换
日志脱敏、替换敏感信息
python
print(re.sub(r'\d+', '*', 'phone:13800138000'))
# phone:***********
5. re.split () 分割
python
print(re.split(r'[,; ]', 'a,b;c d'))
# ['a','b','c','d']
6. re.compile () 预编译
多次匹配时提升性能
python
pat = re.compile(r'\d+')
print(pat.findall('123 abc 456')) # ['123','456']
八、标志位(flags)
re.I:忽略大小写re.S:使.匹配换行re.M:多行模式,^$匹配每行开头结尾
示例:
python
print(re.findall(r'hello', 'Hello hello', re.I))
# ['Hello', 'hello']
s = 'a\nb'
print(re.findall(r'a.b', s, re.S)) # ['a\nb']
九、高频实战场景(测试开发必备)
1. 提取手机号
python
re.findall(r'1[3-9]\d{9}', text)
2. 提取邮箱
python
re.findall(r'[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}', text)
3. 提取 IP
python
re.findall(r'\d+\.\d+\.\d+\.\d+', text)
4. 提取接口返回中的 token
python
re.search(r'"token":"(.*?)"', text).group(1)
5. 日志提取耗时
python
re.search(r'time:(\d+)ms', log).group(1)
十、易错点总结
- 正则字符串建议用 r'' 原始字符串,避免转义灾难
.默认不匹配换行,需要re.S- 贪婪匹配会吞内容,提取标签用
.*? match只匹配开头,search全文搜索- 分组编号从 1 开始,0 是整体匹配