Python 正则表达式
Python 正则表达式
正则表达式(Regular Expression,简称 regex)是一种用于匹配字符串中字符组合的模式。它由普通字符和特殊字符(元字符)组成,是处理文本的利器。
为什么要学习正则表达式?
在实际开发中,经常需要查找符合某些复杂规则的字符串,例如:
-
验证用户输入的手机号、邮箱、身份证号
-
从网页中提取图片地址、链接
-
日志文件中的敏感信息过滤
-
数据清洗中的格式转换
正则表达式可以高效、灵活地完成这些任务。虽然它的语法初看有些晦涩,但一旦掌握,将极大提升文本处理效率。
正则表达式的基本概念
什么是正则表达式?
正则表达式是一种模式,用来描述一类字符串的规则。例如:
-
0\d{2}-\d{8} 可以匹配座机号码(如 010-12345678)
-
\w+@\w+.com 可以匹配简单的邮箱地址
正则表达式并不是 Python 独有的,在 Java、JavaScript、PHP、Go 等语言中都有支持。
正则表达式的功能
| 功能 | 说明 |
|---|---|
| 数据验证 | 检查字符串是否符合某种格式(手机号、邮箱、IP地址) |
| 数据检索 | 在大段文本中快速找到匹配的子串 |
| 数据提取 | 从字符串中提取符合条件的信息 |
| 数据替换 | 将匹配的内容替换为其他字符串 |
| 数据隐藏 | 如将手机号中间几位显示为 (135***6235) |
| 数据过滤 | 过滤敏感词或非法字符 |
Python 中的 re 模块
Python 使用内置的 re 模块来处理正则表达式。
使用步骤
-
导入模块:import re
-
调用匹配函数:如 re.match(), re.search(), re.findall() 等
-
处理匹配结果:根据返回值进行后续操作
常用匹配函数
| 函数 | 说明 | 返回值 |
|---|---|---|
| re.match(pattern, string, flags=0) | 从字符串开头匹配,只返回第一个匹配 | 匹配对象 或 None |
| re.search(pattern, string, flags=0) | 在整个字符串中搜索,只返回第一个匹配 | 匹配对象 或 None |
| re.findall(pattern, string, flags=0) | 在整个字符串中搜索,返回所有匹配 | 列表(匹配成功)或空列表 |
注意:
-
match 和 search 成功时返回的是匹配对象,需要通过 group() 方法提取匹配的字符串。
-
findall 直接返回字符串列表,无需调用 group()。
import re
# match 示例
result = re.match(r'itheima', 'itheima.com')
if result:
print(result.group()) # itheima
# search 示例
result = re.search(r'itheima', 'www.itheima.com')
if result:
print(result.group()) # itheima
# findall 示例
result = re.findall(r'itheima', 'www.itheimaitheima.com')
print(result) # ['itheima', 'itheima']
正则表达式的匹配规则
匹配单个字符
| 元字符 | 说明 | 等价于 | 示例 |
|---|---|---|---|
| . | 匹配除换行符 \n 以外的任意一个字符 | a.c 可匹配 abc、a&c | |
| [ ] | 匹配方括号中列举的任意一个字符 | [abc] 匹配 a 或 b 或 c | |
| [^ ] | 匹配不在方括号中的任意一个字符 | [^abc] 匹配除 a,b,c 外的字符 | |
| \d | 匹配任意一个数字,等价于 [0-9] | [0-9] | |
| \D | 匹配任意一个非数字,等价于 [^0-9] | [^0-9] | |
| \s | 匹配任意一个空白字符(空格、\t、\n等) | [ \t\n\r\f\v] | |
| \S | 匹配任意一个非空白字符 | [^ \t\n\r\f\v] | |
| \w | 匹配任意一个单词字符(字母、数字、下划线、汉字) | [a-zA-Z0-9_](Python3 支持汉字) | |
| \W | 匹配任意一个非单词字符 | [^a-zA-Z0-9_] |
记忆技巧:
-
d → digit(数字)
-
s → space(空白)
-
w → word(单词)
import re
def show(result):
if result:
print("匹配成功:", result.group())
else:
print("匹配失败")
# . 匹配任意字符(除\n)
result = re.match(r'.inzi.6.6_帅.', 'binzi_666_帅气')
show(result) # 成功
# [] 匹配括号中任意一个
result = re.match(r'[bB]inzi', 'Binzi')
show(result) # 成功
# \d 匹配数字
result = re.match(r'binzi_\d\d\d', 'binzi_888')
show(result)
# \D 匹配非数字
result = re.match(r'\Dinzi\D', 'binzi_') # 注意:\D 不能匹配数字
show(result) # 成功,第一个字符 b 不是数字,最后一个 _ 不是数字
# \s 匹配空白
result = re.match(r'binzi_666\s帅气', 'binzi_666 帅气')
show(result)
# \w 匹配单词字符(字母、数字、下划线、汉字)
result = re.match(r'\winzi\w', 'binzi_')
show(result)
匹配多个字符
| 元字符 | 说明 | 次数 |
|---|---|---|
| * | 匹配前一个字符 0 次或无限次 | ≥0 |
| + | 匹配前一个字符 1 次或无限次 | ≥1 |
| ? | 匹配前一个字符 0 次或 1 次 | 0 或 1 |
| {m} | 匹配前一个字符 恰好 m 次 | = m |
| {m,n} | 匹配前一个字符 至少 m 次,至多 n 次 | m ≤ 次数 ≤ n |
# * 示例:第一个字母大写,后面任意字符(0个或多个)
result = re.match(r'[A-Z].*', 'Binzi666')
print(result.group()) # Binzi666
# + 示例:t 和 o 中间至少一个字符
result = re.match(r't.+o', 'tbo')
print(result.group()) # tbo
result = re.match(r't.+o', 'to') # None(至少一个)
# ? 示例:http 或 https
result = re.match(r'https?', 'http')
print(result.group()) # http
result = re.match(r'https?', 'https')
print(result.group()) # https
# {m} 示例:6位密码
result = re.match(r'[a-zA-Z0-9_]{6}', 'abc123')
print(result.group()) # abc123
# {m,n} 示例:8-10位密码
result = re.match(r'[a-zA-Z0-9_]{8,10}', 'abcABC123')
print(result.group()) # abcABC123
匹配开头和结尾
| 元字符 | 说明 |
|---|---|
| ^ | 匹配字符串的开头 |
| $ | 匹配字符串的结尾 |
通常 ^ 和 $ 一起使用,来精确限制整个字符串的格式(如验证手机号、密码长度等)。
# 密码必须为6位,不能多也不能少
result = re.match(r'^[a-zA-Z0-9_]{6}$', 'abc123')
print(result.group()) # abc123
result = re.match(r'^[a-zA-Z0-9_]{6}$', 'abc_123')
print(result) # None(长度为7)
分组
使用小括号 () 可以将正则表达式的一部分分组,以便后续引用或提取。
| 语法 | 说明 | 举例 |
|---|---|---|
| (pattern) | 创建一个分组,编号从 1 开始 | re.search(r’(\d+)-(\d+)', ‘12-25’).group(1) → ‘12’ |
| \数字 | 引用编号为数字的分组(用于匹配相同内容) | re.search(r’(\w+):\1’, ‘apple:apple’).group(1) → ‘apple’ |
| (?P<name>pattern) | 给分组起别名 | re.search(r’(?P\d{4})', ‘2024’).group(‘year’) → ‘2024’ |
| (?P=name) | 引用别名为 name 的分组(必须前面有叫name的分组) | re.search(r’(?P\w+)😦?P=word)', ‘apple:apple’).group(‘word’) → ‘apple’ |
# 分组提取
result = re.match(r'([a-zA-Z0-9_]{4,20})@(163|qq|sina)\.com', 'binzi@qq.com')
if result:
print(result.group(0)) # 整个匹配:binzi@qq.com
print(result.group(1)) # 用户名:binzi
print(result.group(2)) # 域名:qq
# 分组引用(匹配标签)
result = re.match(r'<([a-zA-Z1-6]+)>.*</\1>', '<html>内容</html>')
print(result.group()) # <html>内容</html>
# 别名方式
result = re.match(r'<(?P<tag>[a-zA-Z1-6]+)>.*</(?P=tag)>', '<html>内容</html>')
print(result.group())
特殊匹配(^ 在 [] 中的含义)
当 ^ 出现在方括号 [ ] 的开头时,表示取反,即匹配不在括号中的任意一个字符。
# [^\d] 等价于 \D
result = re.match(r'[^\d].*', 'hello')
print(result.group()) # hello
综合练习
匹配微博话题
import re
def show(data):
if data:
print("匹配成功:", data.group())
else:
print("匹配失败")
# 话题格式:#内容#
result = re.match(r'#.+#', '#幸福是奋斗出来的#')
show(result) # 成功
匹配11位手机号码
# 简单版:1开头,后面10位数字
result = re.match(r'^1[0-9]{10}$', '18866668888')
show(result)
匹配163邮箱地址
# 用户名4-20位,@163.com,注意.需要转义
result = re.match(r'^[a-zA-Z0-9_]{4,20}@163\.com$', 'binzi@163.com')
show(result)
贪婪模式与非贪婪模式
概念
-
贪婪模式:在匹配成功的前提下,尽可能多地匹配字符。默认情况下,*、+、?、{m,n} 都是贪婪的。
-
非贪婪模式:在匹配成功的前提下,尽可能少地匹配字符。在量词后面加上 ? 即可切换为非贪婪模式。
| 贪婪(尽可能多) | 非贪婪(尽可能少) |
|---|---|
| * | *? |
| + | +? |
| ? | ?? |
| {m,n} | {m,n}? |
-
* → *?:匹配前一个字符 0 次或多次,但非贪婪时,一旦匹配成功就停止(优先取 0 次,除非必须取更多才能匹配整个模式)。
-
+ → +?:匹配前一个字符 1 次或多次,非贪婪时优先取 1 次。
-
? → ??:匹配前一个字符 0 次或 1 次,非贪婪时优先取 0 次。
-
{m,n} → {m,n}?:匹配前一个字符 至少 m 次,至多 n 次,非贪婪时优先取 m 次。
示例
import re
text = '<h1>编程语言</h1>此处省略很多内容<h1>python大数据</h1>'
# 贪婪模式:匹配尽可能多的内容
result = re.findall(r'<h1>.*</h1>', text)
print(result) # ['<h1>编程语言</h1>此处省略很多内容<h1>python大数据</h1>']
# 非贪婪模式:匹配尽可能少的内容
result = re.findall(r'<h1>.*?</h1>', text)
print(result) # ['<h1>编程语言</h1>', '<h1>python大数据</h1>']
# 提取标签内的文字(结合分组)
result = re.findall(r'<h1>(.*?)</h1>', text)
print(result) # ['编程语言', 'python大数据']
正则修饰符(标志位)
修饰符用于改变正则表达式的默认行为,作为 match、search、findall 等函数的第三个参数。
| 修饰符 | 说明 |
|---|---|
| re.I (IGNORECASE) | 匹配时不区分大小写 |
| re.S (DOTALL) | 使 . 能够匹配换行符 \n |
| re.M (MULTILINE) | 多行匹配,影响 ^ 和 $ 的行为 |
| re.U (UNICODE) | 根据 Unicode 字符集解析(Python3 默认) |
# re.I 不区分大小写
result = re.findall(r'^a8D9$', 'A8d9', re.I)
print(result) # ['A8d9']
# re.S 让 . 匹配换行符
text = '123\n456\n789'
result = re.match(r'.{7}', text, re.S)
if result:
print(result.group()) # 123\n456(包含换行符)
# 多个正则修饰符,用 | 连接
text = "Hello\nWorld"
result = re.findall(r"^h.*d$", text, re.I | re.M | re.S)
print(result)
常用正则表达式参考(扩展)
| 用途 | 正则表达式 |
|---|---|
| 手机号(简单) | ^1[3-9]\d{9}$ |
| 邮箱(简单) | ^\w+@\w+.\w+$ |
| 身份证号(18位) | ^\d{17}[\dXx]$ |
| 日期(YYYY-MM-DD) | ^\d{4}-\d{2}-\d{2}$ |
| IP 地址(IPv4) | ^((25[0-5]|2[0-4]\d|1\d{2}|[1-9]?\d).){3}(25[0-5]|2[0-4]\d|1\d{2}|[1-9]?\d)$ |
| URL | https?😕/[\s]+$ |
提示:在实际开发中,可以借助在线正则表达式测试工具(如 https://c.runoob.com/front-end/854/)快速验证正则表达式。
总结
| 类别 | 关键点 |
|---|---|
| 匹配函数 | match(开头匹配)、search(全文搜索)、findall(返回所有匹配) |
| 单字符匹配 | .、[]、\d、\D、\s、\S、\w、\W |
| 多字符匹配 | *、+、?、{m}、{m,n} |
| 边界匹配 | ^(开头)、$(结尾) |
| 分组 | () 提取子串、\数字 或 (?P) 引用分组 |
| 贪婪/非贪婪 | 默认贪婪,量词后加 ? 变为非贪婪 |
| 修饰符 | re.I(忽略大小写)、re.S(. 匹配换行) |
更多推荐


所有评论(0)