目录
正则表达式(Regular Expression,简称 regex) 是一种用于描述文本模式的强大工具,能够高效地进行字符串的匹配、查找、替换和分割等操作。它通过一系列特殊符号和规则,帮助开发者在文本中精准定位符合特定规则的内容。正则表达式有2个核心概念:
模式匹配
正则表达式通过定义一个“模式”(Pattern),去匹配与之相符的字符串。例如:- 模式
r'\d{3}'
可以匹配任意连续的三个数字(如"123"
)。 - 模式
r'^[a-zA-Z]+$'
可以匹配仅由字母组成的字符串(如"Hello"
)。
- 模式
Python 中的实现
Python 通过内置的re
模块支持正则表达式。
现在分别介绍这2个核心概念:
7.1 模式匹配字符串
正则表达式的核心在于其模式匹配语法,下面详细介绍模式匹配字符串的相关概念。
7.1.1 普通字符与转义
普通字符直接匹配其字面内容,例如:
模式:'hello'
匹配:'hello world' 中的 'hello'
不匹配:'hi'、'HELLO'
转义字符 \
用于取消元字符的特殊含义,或表示特殊字符:
模式:'\.'
匹配:句点 '.'(如 'a.b' 中的 '.')
模式:'\\d'
匹配:反斜杠后接字母 'd'(如 'a\db' 中的 '\d')
7.1.2 元字符(特殊字符)
元字符是正则中具有特殊功能的字符,常见元字符如下:
.
匹配任意单个字符(除换行符)^
匹配字符串开头$
匹配字符串结尾*
匹配前一个字符0次或多次+
匹配前一个字符1次或多次?
匹配前一个字符0次或1次{m,n}
匹配前一个字符m到n次[]
字符集,匹配其中任意一个字符|
或操作,匹配左边或右边()
分组,捕获匹配内容
1. 匹配任意字符:.
- 功能:匹配除换行符
\n
外的任意单个字符。
模式:'h.t'
匹配:'hat'、'hot'、'h t'、'hit'(中间字符可以是任意字符)
不匹配:'hatt'(长度为4,模式要求3个字符)
2. 锚定位置:^
和 $
^
:匹配字符串的开头。$
:匹配字符串的结尾。
模式:'^hello'
匹配:'hello world'(以 'hello' 开头)
不匹配:'say hello'
模式:'world$'
匹配:'hello world'(以 'world' 结尾)
不匹配:'world cup'
3. 重复匹配:*
、+
、?
、{}
*
:匹配前一个字符 0 次或多次。
模式:'ab*'
匹配:'a'(b出现0次)、'ab'、'abb'、'abbb'...
+
:匹配前一个字符 1 次或多次。
模式:'ab+'
匹配:'ab'、'abb'、'abbb'...
不匹配:'a'(b至少出现1次)
?
:匹配前一个字符 0 次或 1 次(可选匹配)。
模式:'colou?r'
匹配:'color'(u出现0次)、'colour'(u出现1次)
{n}
、{n,}
、{n,m}
:精确控制重复次数。
模式:'a{3}'
匹配:'aaa'
不匹配:'aa'、'aaaa'
模式:'a{2,}'
匹配:'aa'、'aaa'、'aaaa'...(至少2个a)
模式:'a{1,3}'
匹配:'a'、'aa'、'aaa'(1到3个a)
4.字符集与范围 []
1. 自定义字符集:[]
- 功能:匹配方括号内的任意一个字符。
模式:'[aeiou]'
匹配:任意元音字母(如 'apple' 中的 'a'、'e')
模式:'[0-9]'
匹配:任意数字(等价于 `\d`)
模式:'[a-zA-Z0-9]'
匹配:任意字母或数字
2. 排除字符集:[^]
- 功能:匹配不在方括号内的任意字符。
模式:'[^0-9]'
匹配:任意非数字字符(等价于 `\D`)
模式:'[^aeiou]'
匹配:任意非元音字母
5. 或操作:|
- 功能:匹配多个模式中的任意一个。
模式:'(cat|dog)'
匹配:'cat' 或 'dog'
模式:'a(b|c)d'
匹配:'abd' 或 'acd'
6. 分组:()
- 功能:将模式分组,便于整体操作或后续引用。分组有2种模式,捕获分组和非捕获分组,相关内容介绍如下:
在 Python 正则表达式中,捕获分组(Capturing Group) 和 非捕获分组(Non-capturing Group) 是用于组织和控制匹配逻辑的重要工具。它们的主要区别在于是否将匹配内容保存下来,供后续使用(如提取、替换等)。
1. 捕获分组 (Capturing Groups)
捕获分组是正则表达式中用普通圆括号 ()
表示的分组,它会捕获匹配的内容并分配一个组号,后续可以通过组号引用这些匹配内容。格式如下:
(pattern)
特点:
会记住匹配的内容
可以通过索引(从1开始)或名称(如果命名了)引用
会增加正则表达式引擎的处理开销
示例 1:提取年月日
import re
text = "今天是2024-04-05"
match = re.match(r'(\d{4})-(\d{2})-(\d{2})', text)
if match:
print(match.groups()) # ('2024', '04', '05')
print(match.group(1)) # '2024'
print(match.group(2)) # '04'
print(match.group(3)) # '05'
示例 2:命名捕获分组
match = re.match(r'(?P<year>\d{4})-(?P<month>\d{2})-(?P<day>\d{2})', "2024-04-05")
if match:
print(match.group('year')) # '2024'
print(match.group('month')) # '04'
print(match.group('day')) # '05'
2.非捕获分组(Non-capturing Group)
语法:
(?:pattern)
- 使用
(?:...)
包裹的表达式是非捕获分组。 不记住匹配的内容
无法通过组号引用
性能优于捕获分组
适用于只需要分组但不需要提取内容的场景
示例 1:匹配电话号码格式
text = "电话:010-12345678"
match = re.match(r'.*(?:\d{3}-)?\d{8}', text)
if match:
print(match.group()) # 电话:010-12345678
print(match.groups()) # () # 没有捕获分组
示例 2:与捕获分组混合使用
text = "访问地址:https://example.com"
match = re.match(r'(https?)://(?:www\.)?(\w+\.\w+)', text)
if match:
print(match.groups()) # ('https', 'example.com')
print(match.group(1)) # 'https'
print(match.group(2)) # 'example.com'
3. 捕获分组 vs 非捕获分组对比示例
import re
text = "Product: Laptop, Price: 999.99"
# 使用捕获分组
pattern_capture = r'Product: (\w+), Price: (\d+\.\d{2})'
match_capture = re.search(pattern_capture, text)
# match_capture.groups() = ('Laptop', '999.99')
# match_capture.group(1) = 'Laptop'
# match_capture.group(2) = '999.99'
# 使用非捕获分组
pattern_non_capture = r'Product: (?:\w+), Price: (\d+\.\d{2})'
match_non_capture = re.search(pattern_non_capture, text)
# match_non_capture.groups() = ('999.99',)
# match_non_capture.group(1) = '999.99'
# 无法获取产品名称,因为它被非捕获分组包裹
4. 反向引用:\1
、\2
- 功能:引用前面分组匹配到的内容。
模式:r'(\w+) \1'
匹配:'hello hello'、'123 123'
不匹配:'hello world'
文本:'abccba'、'123321'
模式:r'(\w)(\w)(\w)\3\2\1'
匹配:'abccba'、'123321'
解释:
- 第1个分组 (\w) 匹配 'a' → \1 代表 'a'
- 第2个分组 (\w) 匹配 'b' → \2 代表 'b'
- 第3个分组 (\w) 匹配 'c' → \3 代表 'c'
- 后续 \3\2\1 要求与前面三个字符对称
7.1.3 预定义字符集
字符集 | 含义 | 等价于 |
---|---|---|
\d |
匹配任意数字 | [0-9] |
\D |
匹配任意非数字 | [^0-9] |
\w |
匹配单词字符(字母、数字、下划线) | [a-zA-Z0-9_] |
\W |
匹配非单词字符 | [^a-zA-Z0-9_] |
\s |
匹配空白字符 | [ \t\n\r\f\v] |
\S |
匹配非空白字符 | [^ \s] |
\b | 匹配单词的开始或结束 |
模式:'\d{3}-\d{4}'
匹配:电话号码格式,如 '123-4567'
模式:'\w+@\w+[-.\w]*\.\w+'
匹配:邮箱格式,如 'user.name@example.co.uk'
下面详细介绍一下\b:
\b 是正则表达式中一个特殊的单词边界(word boundary)匹配符,它用于匹配单词的边界位置,而不是匹配实际的字符。
核心概念
- 单词边界:指字母、数字、下划线(
\w
) 与 非字母、数字、下划线(\W
) 之间的位置,包括字符串的开头或结尾。 \w
匹配[a-zA-Z0-9_]
,\W
是其补集(如空格、标点等)。- 零宽度断言:
\b
不消耗任何字符,只匹配位置(类似于^
和$
)
常见匹配以下三种位置:
单词字符与非单词字符之间(如
"a b"
中的a
和空格之间)字符串开头与单词字符之间(如
"abc"
的开头)单词字符与字符串结尾之间(如
"abc"
的结尾)
匹配规则
位置类型 | 示例字符串 | \b 匹配的位置 |
---|---|---|
单词字符与非单词字符之间 | cat! |
在 cat 和 ! 之间 |
非单词字符与单词字符之间 | !cat |
在 ! 和 cat 之间 |
字符串开头与单词字符之间 | cat |
在字符串开头和 c 之间 |
单词字符与字符串结尾之间 | cat |
在 t 和字符串结尾之间 |
典型用法
精确匹配单词:避免部分匹配,例如匹配完整单词 cat
,而非 category
中的 cat
:
re.search(r'\bcat\b', 'cat') # 匹配
re.search(r'\bcat\b', 'category') # 不匹配
分隔符处理:匹配以特定符号分隔的单词:
re.findall(r'\b\w+\b', 'hello, world!') # 输出 ['hello', 'world']
替换特定单词:替换完整单词而不影响其他部分:
re.sub(r'\bapple\b', 'orange', 'apple pie') # 输出 'orange pie'
注意事项
不匹配空格:
\b
匹配的是位置,而非空格或标点。例如在cat dog
中,\b
匹配cat
和空格之间,而非空格本身。特殊字符的影响:连字符(
-
)和撇号('
)通常被视为非单词字符,例如mother-in-law
中的-
会形成边界。Unicode 支持:在 Python 的
re
模块中,\w
和\b
默认仅支持 ASCII 字符(如a-z
),若需支持 Unicode(如中文),需使用re.UNICODE
标志。
对比示例
正则表达式 | 匹配字符串 | 结果 |
---|---|---|
cat |
category |
匹配 cat |
\bcat\b |
category |
不匹配 |
\bcat\b |
The cat sat |
匹配 cat |
\bcat\b |
cat's |
匹配 cat |
总结:\b
是用于定位单词边界的断言工具,常用于精确匹配完整单词或处理文本分隔逻辑。理解其匹配规则可避免误匹配或漏匹配。
7.1.4 贪婪匹配 和 非贪婪匹配
在 Python 正则表达式中,贪婪匹配(Greedy Matching) 和 非贪婪匹配(Non-greedy Matching) 是两种重要的匹配策略,它们决定了正则引擎在面对多个可能匹配时如何选择。
7.1.4.1 贪婪匹配(Greedy Matching)
定义:
- 贪婪匹配是正则表达式的默认行为。
- 它会尽可能多地匹配符合规则的内容,直到无法继续匹配为止。
常见贪婪量词:
量词 | 含义 |
---|---|
* |
匹配前一个元素 0 次或多次 |
+ |
匹配前一个元素 1 次或多次 |
? |
匹配前一个元素 0 次或 1 次 |
{n,m} |
匹配前一个元素 n 到 m 次 |
示例 1:贪婪匹配 <.*>
匹配 HTML 标签
import re
text = "<p>hello</p>"
match = re.match(r'<.*>', text)
print(match.group()) # 输出: <p>hello</p>
示例 2:贪婪匹配 a.*b
匹配最长字符串
text = "abcbd"
match = re.match(r'a.*b', text)
print(match.group()) # 输出: abcb
7.1.4.2 非贪婪匹配(Non-greedy Matching)
定义:
- 非贪婪匹配(也称为“懒惰匹配”)会尽可能少地匹配内容。
- 在贪婪量词后加
?
可将其变为非贪婪模式。
常见非贪婪量词:
量词 | 含义 |
---|---|
*? |
匹配前一个元素 0 次或多次(最少) |
+? |
匹配前一个元素 1 次或多次(最少) |
?? |
匹配前一个元素 0 次或 1 次(最少) |
{n,m}? |
匹配前一个元素 n 到 m 次(最少) |
示例 1:非贪婪匹配 <.*?>
匹配 HTML 标签
text = "<p>hello</p>"
match = re.match(r'<.*?>', text)
print(match.group()) # 输出: <p>
示例 2:非贪婪匹配 a.*?b
匹配最短字符串
text = "abcbd"
match = re.match(r'a.*?b', text)
print(match.group()) # 输出: ab
7.1.4.3 总结
特性 | 贪婪匹配(Greedy) | 非贪婪匹配(Non-greedy) |
---|---|---|
默认行为 | 是 | 否(需加 ? ) |
匹配策略 | 尽可能多 | 尽可能少 |
适用场景 | 匹配完整结构(如整个 HTML 标签) | 匹配最小结构(如单个 HTML 标签) |
量词形式 | * , + , ? , {n,m} |
*? , +? , ?? , {n,m}? |
常见误区:
非贪婪并不总是匹配最短结果:它只是在当前匹配过程中尽可能少匹配,但不一定是最短的全局匹配。
贪婪可能导致意外的长匹配:例如在解析 HTML 或 JSON 时,贪婪模式可能匹配到不期望的内容。
最佳实践:
在需要精确匹配最小单位时使用非贪婪(如提取 HTML 标签)。
在需要完整匹配整个结构时使用贪婪(如提取整个 JSON 对象)。
7.2 re模块介绍
Python 的 re
模块是用于处理正则表达式的核心工具。它提供了多种方法来执行字符串匹配、查找、替换和分割等操作。以下是 re
模块的常用函数及其使用示例。 使用之前需要先导入re模块。
import re # 导入re模块
下面介绍re模块的一些常用操作。
7.2.1 re.match():从字符串开头匹配
re.match() 是 Python 正则表达式模块中用于从字符串开头匹配模式的核心函数。它尝试从字符串的起始位置应用正则表达式模式,如果匹配成功则返回匹配对象,否则返回 None。
7.2.1.1 基础语法
re.match(pattern, string, flags=0)
pattern: 正则表达式模式(字符串)
string: 要匹配的目标字符串
flags: 可选标志,用于修改匹配行为(如忽略大小写、多行模式等)
返回值
匹配成功:返回一个 Match 对象
匹配失败:返回 None
核心特性
起始位置匹配:只检查字符串开头是否匹配
单次匹配:只返回第一个匹配结果
非贪婪:匹配行为不受全局标志影响
7.2.1.2 Match 对象常用方法
方法 | 说明 |
---|---|
group() 或 group(0) |
返回完整匹配的字符串 |
group(n) |
返回第 n 个分组的匹配内容 |
groups() |
返回所有分组的匹配内容(元组形式) |
start() / end() |
返回匹配的起始和结束位置索引 |
span() |
返回匹配的起止位置(元组) |
text = 'abc123def'
match = re.match(r'[a-z]+(\d+)', text)
if match:
print("完整匹配:", match.group()) # 输出: 完整匹配: abc123
print("分组匹配:", match.group(1)) # 输出: 分组匹配: 123
print("起始位置:", match.start()) # 输出: 起始位置: 0
print("结束位置:", match.end()) # 输出: 结束位置: 6
print("起止范围:", match.span()) # 输出: 起止范围: (0, 6)
7.2.1.3 基础用法示例
示例1:基础匹配
import re
# 匹配以数字开头的字符串
pattern = r'\d+'
text = '123abc'
match = re.match(pattern, text)
if match:
print("匹配结果:", match.group()) # 输出: 匹配结果: 123
示例2:没有从头开始匹配
# 匹配以数字开头的字符串(但实际字符串以字母开头)
text = 'abc123'
match = re.match(r'\d+', text)
print("匹配结果:", match) # 输出: 匹配结果: None
示例3:使用分组提取
text = "2023-05-15"
result = re.match(r'(\d{4})-(\d{2})-(\d{2})', text)
if result:
print(result.group()) # '2023-05-15'
print(result.group(1)) # '2023' - 第一个分组
print(result.group(2)) # '05' - 第二个分组
print(result.group(3)) # '15' - 第三个分组
print(result.groups()) # ('2023', '05', '15')
print(result.span(1)) # (0, 4) - 年份位置
7.2.1.4 使用标志 (flags)
flags(标志)是 Python re
模块中用于修改正则表达式匹配行为的可选参数。它们可以单独使用,也可以通过按位或 (|
) 组合使用。
常用 flags 列表:
标志常量 | 缩写 | 描述 |
---|---|---|
re.IGNORECASE |
re.I |
忽略大小写 |
re.MULTILINE |
re.M |
多行模式(影响 ^ 和 $) |
re.DOTALL |
re.S |
使 . 匹配包括换行符在内的所有字符 |
re.VERBOSE |
re.X |
允许编写更易读的正则表达式(可添加注释和空白) |
re.ASCII |
re.A |
使 \w, \W, \b, \B, \s, \S 只匹配 ASCII 字符 |
re.LOCALE |
re.L |
使 \w, \W, \b, \B, \s, \S 依赖当前区域设置(Python 3.6+ 不推荐) |
re.DEBUG |
- | 显示编译时的调试信息 |
常见的是re.I,re.M,re.S,现在对这三个给出示例:
1. re.IGNORECASE (re.I) - 忽略大小写
import re
text = "Python is awesome, PYTHON is powerful"
matches = re.findall(r'python', text, re.IGNORECASE)
print(matches) # ['Python', 'PYTHON']
# 验证用户名(不区分大小写)
username = "Admin"
if re.match(r'^admin$', username, re.I):
print("管理员账号")
2. re.MULTILINE (re.M) - 多行模式
改变 ^
和 $
的行为,使它们分别匹配每一行的开头和结尾,而不是整个字符串的开头和结尾。
text = """第一行
第二行
第三行"""
# 不使用 MULTILINE
matches = re.findall(r'^\w+', text)
print(matches) # ['第一行']
# 使用 MULTILINE
matches = re.findall(r'^\w+', text, re.MULTILINE)
print(matches) # ['第一行', '第二行', '第三行']
# 匹配每行末尾的数字
log = """error: 404
warning: 301
info: 200"""
codes = re.findall(r'\d+$', log, re.M)
print(codes) # ['404', '301', '200']
3. re.DOTALL (re.S) - 点号匹配所有字符
使 .
匹配包括换行符在内的所有字符(默认不匹配换行符)。
text = """start
end"""
# 默认情况(. 不匹配换行符)
match = re.search(r'start.*end', text)
print(match) # None
# 使用 DOTALL
match = re.search(r'start.*end', text, re.DOTALL)
print(match.group()) # 'start\nend'
# 提取多行HTML标签内容
html = "<div>第一段\n第二段</div>"
content = re.search(r'<div>(.*?)</div>', html, re.S).group(1)
print(content) # '第一段\n第二段'
7.2.2 re.search() - 扫描整个字符串
re.search() 是 Python re 模块中用于查找字符串中任意位置匹配正则表达式的函数。与 re.match() 不同,它不局限于字符串开头,而是扫描整个字符串,返回第一个匹配的结果。如果未找到匹配项,则返回 None。
re.search()的语法和re.match完全一样,函数参数的含义也一样,返回的match对象使用方法也一致。
re.search(pattern, string, flags=0)
参数 | 说明 |
---|---|
pattern |
正则表达式模式(字符串或已编译的 Pattern 对象) |
string |
要匹配的目标字符串 |
flags |
可选标志位(如 re.IGNORECASE 忽略大小写) |
返回值
匹配成功:返回一个 Match 对象,包含匹配结果、分组、位置等信息。
匹配失败:返回 None
- 默认区分大小写:除非使用
re.IGNORECASE
标志。
核心特性
全局扫描:检查整个字符串而不仅是开头
单次匹配:只返回第一个匹配结果
灵活定位:可以在字符串任意位置找到匹配
import re
# 在字符串中间匹配数字
pattern = r'\d+'
text = 'abc123def456'
match = re.search(pattern, text)
if match:
print("匹配结果:", match.group()) # 输出: 匹配结果: 123
# 尝试匹配不存在的模式
match = re.search(r'xyz', 'abc123')
print("匹配结果:", match) # 输出: 匹配结果: None
7.2.3 re.findall() - 查找所有匹配项
re.findall() 是 Python re 模块中用于查找字符串中所有匹配正则表达式的子串的函数。与 re.match() 和 re.search() 不同,它不局限于单个匹配,而是返回所有匹配结果的列表。如果没有找到匹配项,则返回空列表。
7.2.3.1 基本语法
与前面2个函数一样,re.findall() 语法和前面函数语法完全一致。
re.findall(pattern, string, flags=0)
参数 | 说明 |
---|---|
pattern |
正则表达式模式(字符串或已编译的 Pattern 对象) |
string |
要匹配的目标字符串 |
flags |
可选标志位(如 re.IGNORECASE 忽略大小写) |
返回值
当模式没有分组时:返回所有完整匹配的字符串列表
当模式有分组时:返回所有分组组成的元组列表
没有匹配时:返回空列表
核心特性
全局扫描:检查整个字符串的所有匹配
返回所有结果:不止是第一个匹配
分组影响结果:有无分组返回不同结构的数据
非重叠匹配:匹配结果不会重叠
7.2.3.2 代码示例
1. 简单查找(无分组)
import re
# 查找所有数字
text = "苹果12个,香蕉5根,橙子8个"
numbers = re.findall(r'\d+', text)
print(numbers) # ['12', '5', '8']
# 查找所有单词
text = "Hello world, Python is awesome"
words = re.findall(r'\w+', text)
print(words) # ['Hello', 'world', 'Python', 'is', 'awesome']
2. 使用分组查找
# 查找带单位的数字
text = "距离: 3.5km, 时间: 45min"
results = re.findall(r'(\d+\.?\d*)(\w+)', text)
print(results) # [('3.5', 'km'), ('45', 'min')]
# 解析日志时间戳
log = "[2023-05-15 14:30:22] [2023-05-16 09:15:33]"
timestamps = re.findall(r'\[(\d{4}-\d{2}-\d{2}) (\d{2}:\d{2}:\d{2})\]', log)
print(timestamps)
# [('2023-05-15', '14:30:22'), ('2023-05-16', '09:15:33')]
- 无分组:返回匹配的完整子串列表。
- 有分组:
- 如果只有一个分组,返回匹配的分组内容列表。
- 如果有多个分组,返回元组列表,每个元组包含各分组的匹配内容。
# 示例:单一分组
text = "abc123def456"
pattern = r"(\d+)" # 只有一个分组
result = re.findall(pattern, text)
print(result)
# 输出: ['123', '456']
# 示例:多个分组
text = "name:Tom, age:25; name:Jerry, age:30"
pattern = r"name:(.*?), age:(\d+)" # 两个分组
result = re.findall(pattern, text)
print(result)
# 输出: [('Tom', '25'), ('Jerry', '30')]
7.2.4 re.finditer() - 返回迭代器
re.finditer() 是 Python 正则表达式模块 re 中的一个函数,用于在字符串中查找所有与正则表达式匹配的子串,与 re.findall()
返回列表不同,re.finditer()返回一个迭代器(iterator) 。每次迭代会生成一个 Match 对象,包含匹配的详细信息(如匹配内容、位置等)。
基础语法
re.finditer(pattern, string, flags=0)
pattern: 正则表达式模式(字符串或已编译的模式对象)
string: 要搜索的目标字符串
flags: 可选标志,用于修改匹配行为
返回值
返回一个迭代器,其中每个元素都是一个 Match 对象(与 re.search()
返回的对象类型相同)。如果没有匹配项,迭代器将为空。
核心特点
内存高效:不会一次性返回所有结果,适合处理大文本
信息丰富:每个匹配都是完整的 Match 对象,包含位置等信息
惰性求值:只有在迭代时才会查找下一个匹配
非重叠匹配:与
findall()
一样,返回的都是非重叠匹配
import re
text = "There are 3 apples, 5 oranges and 2 bananas"
pattern = r'\d+ \w+'
for match in re.finditer(pattern, text):
print(f"找到 '{match.group()}' 在位置 {match.start()}-{match.end()}")
# 输出:
# 找到 '3 apples' 在位置 10-18
# 找到 '5 oranges' 在位置 20-29
# 找到 '2 bananas' 在位置 34-43
7.2.5 匹配查找函数总结
方法 | 匹配位置 | 返回类型 | 结果数量 | 返回内容 | 适用场景 |
---|---|---|---|---|---|
match() |
从字符串开头 | Match 对象/None |
单个结果 | 首个匹配(若存在) | 验证字符串是否以某模式开头 |
search() |
全局扫描 | Match 对象/None |
单个结果 | 首个匹配(任意位置) | 查找是否存在匹配项 |
findall() |
全局扫描 | 列表 | 所有匹配 | 所有匹配的字符串/分组 | 直接获取所有匹配结果列表 |
finditer() |
全局扫描 | 迭代器(Match 对象) |
所有匹配 | 所有匹配的 Match 对象 |
高效处理大量数据,需逐个解析 |
7.2.6 re.sub()替换字符串
re.sub() 用于通过正则表达式匹配文本,并将匹配内容替换为指定字符串,返回替换后的结果字符串。
7.2.6.1 基本语法
re.sub(pattern, repl, string, count=0, flags=0)
参数 | 说明 |
---|---|
pattern |
正则表达式模式(字符串或编译后的正则对象) |
repl |
替换内容(字符串或函数) |
string |
被处理的原始字符串 |
count |
可选,替换的最大次数(默认 0 表示替换所有匹配项) |
flags |
可选,匹配模式标志(如 re.IGNORECASE ) |
7.2.6.2 替换规则
直接替换:repl
是字符串时,直接替换匹配内容。示例:
re.sub(r'\d+', 'X', 'a1b2c3') # 输出 'aXbXcX'
引用分组:使用 \1
, \2
等引用捕获组内容。示例:
re.sub(r'(\w+) (\w+)', r'\2 \1', 'hello world') # 输出 'world hello'
动态替换(函数) :repl
可以是函数,接收 Match
对象,返回替换字符串。示例:
def to_upper(m):
return m.group().upper()
re.sub(r'\b\w+\b', to_upper, 'hello world') # 输出 'HELLO WORLD'
7.2.6.3 常用示例
简单替换
re.sub(r'apple', 'orange', 'I like apple.') # 输出 'I like orange.'
替换并限制次数
re.sub(r'\d', 'X', 'a1b2c3', count=2) # 输出 'aXbXc3'
过滤敏感词
re.sub(r'傻|笨', '*', '你真傻,他真笨') # 输出 '你真*,他真*'
格式转换
re.sub(r'(\d{4})-(\d{2})-(\d{2})', r'\3/\2/\1', '2023-10-05') # 输出 '05/10/2023'
清除空白字符
re.sub(r'\s+', ' ', ' Hello world ') # 输出 'Hello world'
注意事项
- 贪婪匹配:默认贪婪匹配,可通过正则语法控制(如
.*?
)。 - 特殊字符转义:若
repl
包含$
或\
,需用r''
原始字符串或双重转义。 - 性能优化:频繁调用时建议预编译正则表达式(
re.compile()
)。
7.2.7 re.split()分割字符串
re.split() 是 Python 中 re 模块用于基于正则表达式模式分割字符串的函数。与字符串内置的 split() 方法不同,re.split() 可以使用更灵活的模式定义分割点。以下是对该函数的详细解析:
7.2.7.1 函数定义与参数
re.split(pattern, string, maxsplit=0, flags=0)
pattern
:正则表达式模式,用于匹配分割点。string
:需要分割的目标字符串。maxsplit
(可选):最大分割次数,默认0
表示不限制次数。flags
(可选):标志位,用于控制匹配方式(如忽略大小写、多行匹配等)。
返回值:
- 返回一个列表,包含分割后的子字符串。
7.2.7.2 基本分割用法
示例 1:按空格或逗号分割
import re
text = "Hello, world! Python is fun."
pattern = r'[ ,!]+' # 匹配一个或多个空格、逗号或感叹号
result = re.split(pattern, text)
print(result) # 输出: ['Hello', 'world', 'Python', 'is', 'fun.']
示例 2:按数字分割
text = "a1b22c333d"
pattern = r'\d+' # 匹配一个或多个数字
result = re.split(pattern, text)
print(result) # 输出: ['a', 'b', 'c', 'd']
7.2.7.3 使用分组保留分割符
当正则表达式模式中包含括号分组时,匹配的分割符会被保留在结果列表中。
示例 3:保留分割符
text = "Hello-world-python"
pattern = r'(-)' # 用括号分组包含分割符
result = re.split(pattern, text)
print(result) # 输出: ['Hello', '-', 'world', '-', 'python']
示例 4:复杂模式保留分割符
text = "2023-05-29"
pattern = r'(\D+)' # 匹配非数字字符并分组
result = re.split(pattern, text)
print(result) # 输出: ['2023', '-', '05', '-', '29']
7.2.7.4 限制分割次数
通过 maxsplit
参数可以限制分割的最大次数,剩余部分将作为最后一个元素保留。
示例 5:限制分割次数
text = "apple,banana,orange,grape"
pattern = r',' # 按逗号分割
maxsplit = 2 # 最多分割2次
result = re.split(pattern, text, maxsplit)
print(result) # 输出: ['apple', 'banana', 'orange,grape']
7.2.7.5 使用标志位(Flags)
与其他 re
函数相同,re.split()
支持通过 flags
参数修改匹配规则,常见的有:
- re.IGNORECASE 或 re.I:忽略大小写。
- re.MULTILINE 或 re.M:多行模式,影响 ^ 和 $ 的匹配。
示例 6:忽略大小写分割
text = "Hello-World-Python"
pattern = r'[a-z]+' # 匹配小写字母
# 不使用标志位:按小写字母分割
result1 = re.split(pattern, text)
print(result1) # 输出: ['H', '-W', '-P', '']
# 使用 re.IGNORECASE:按大小写字母分割
result2 = re.split(pattern, text, flags=re.IGNORECASE)
print(result2) # 输出: ['', '-', '-', '']
7.2.7.6. 常见应用场景
解析复杂格式的文本:分割包含多种分隔符的日期:
date = "2023/05-29"
pattern = r'[/-]' # 按斜杠或连字符分割
result = re.split(pattern, date)
print(result) # 输出: ['2023', '05', '29']
分割字符串并保留关键信息:
log = "2023-05-29 10:30:00 INFO: Operation completed"
pattern = r'(\d{4}-\d{2}-\d{2}) (\d{2}:\d{2}:\d{2}) (\w+): ' # 分组捕获日期、时间和日志级别
result = re.split(pattern, log)
print(result) # 输出: ['', '2023-05-29', '10:30:00', 'INFO', 'Operation completed']
分割 HTML 标签
html = "<p>Hello</p><div>World</div>"
pattern = r'(</?[a-z]+>)' # 匹配 HTML 标签
result = re.split(pattern, html)
print(result) # 输出: ['', '<p>', 'Hello', '</p>', '', '<div>', 'World', '</div>', '']
总结
re.split()
提供了基于正则表达式的灵活分割功能,适用于处理复杂格式的文本。- 通过分组可以保留分割符,便于后续处理。
- 若需简单按固定字符分割,可使用字符串的
split()
方法;若分割规则复杂,re.split()
是更好的选择。
7.2.8 re.complie编译正则表达式
re.compile()是Python的re模块中用于预编译正则表达式模式的函数。它可以将一个正则表达式字符串编译为一个re.Pattern对象,从而在后续的匹配、搜索、替换等操作中重复使用,提高效率。
7.2.8.1 函数定义与作用
re.compile(pattern, flags=0)
pattern
:正则表达式字符串。flags
(可选):标志位,用于控制匹配方式(如忽略大小写、多行匹配等)。
返回值:
- 返回一个编译后的正则表达式对象,可用于后续的匹配、搜索、替换等操作。
为什么需要编译正则表达式?
- 性能优化:编译后的正则表达式会被缓存,多次使用时无需重复解析模式,提高效率。
- 代码复用:可在多个地方重复使用同一模式,避免代码冗余。
- 可读性:将复杂的正则表达式单独定义,使代码更易理解。
7.2.8.2 基本用法示例
示例 1:编译后使用匹配方法
import re
# 编译正则表达式
pattern = re.compile(r'\d+') # 匹配一个或多个数字
# 使用编译后的对象进行匹配
text1 = "abc123def"
text2 = "hello456world"
# 等价于 re.search(r'\d+', text1)
match1 = pattern.search(text1)
match2 = pattern.search(text2)
print(match1.group()) # 输出: 123
print(match2.group()) # 输出: 456
示例 2:编译后使用多种方法
pattern = re.compile(r'[a-z]+') # 匹配小写字母
text = "Hello World! Python is fun."
# 使用 findall() 方法
matches = pattern.findall(text)
print(matches) # 输出: ['ello', 'orld', 'ython', 'is', 'fun']
# 使用 split() 方法
split_result = pattern.split(text)
print(split_result) # 输出: ['H', ' ', '! P', ' ', ' ', '.']
7.2.8.3 编译标志位的使用
通过 flags
参数可修改匹配规则,常用标志位:
re.IGNORECASE
或re.I
:忽略大小写。re.MULTILINE
或re.M
:多行模式,影响^
和$
的匹配。re.DOTALL
或re.S
:.
匹配任意字符(包括换行符)。
示例 3:忽略大小写编译
# 编译时指定忽略大小写
pattern = re.compile(r'python', flags=re.IGNORECASE)
text = "Hello Python! PYTHON is great."
matches = pattern.findall(text)
print(matches) # 输出: ['Python', 'PYTHON']
示例 4:多行模式匹配
text = """
Line 1: Data
Line 2: More data
Line 3: Final data
"""
# 编译时指定多行模式
pattern = re.compile(r'^Line (\d+)', flags=re.MULTILINE)
matches = pattern.findall(text)
print(matches) # 输出: ['1', '2', '3']
编译正则表达式在多次使用同一模式时更高效,尤其在处理大量文本时差异明显。
re.compile()
适用于需要多次使用同一正则表达式的场景,可提高性能和代码可读性。- 编译后的对象支持
search()
,match()
,findall()
,split()
,sub()
等所有re
模块的函数。 - 若只需使用一次正则表达式,可直接使用
re
模块的便捷函数(如re.search()
)。