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(. 匹配换行)
Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐