第七章.正则表达式

发布于:2025-06-03 ⋅ 阅读:(26) ⋅ 点赞:(0)

目录

7.1 模式匹配字符串

7.1.1 普通字符与转义

7.1.2 元字符(特殊字符)

7.1.3 预定义字符集

7.1.4 贪婪匹配 和 非贪婪匹配

7.2 re模块介绍

7.2.1 re.match():从字符串开头匹配 

7.2.2 re.search() - 扫描整个字符串

7.2.3 re.findall() - 查找所有匹配项

7.2.4 re.finditer() - 返回迭代器

7.2.5 匹配查找函数总结

7.2.6 re.sub()替换字符串 

7.2.7 re.split()分割字符串

7.2.8 re.complie编译正则表达式

        正则表达式(Regular Expression,简称 regex) 是一种用于描述文本模式的强大工具,能够高效地进行字符串的匹配、查找、替换和分割等操作。它通过一系列特殊符号和规则,帮助开发者在文本中精准定位符合特定规则的内容。正则表达式有2个核心概念:

  1. 模式匹配
    正则表达式通过定义一个“模式”(Pattern),去匹配与之相符的字符串。例如:

    • 模式 r'\d{3}' 可以匹配任意连续的三个数字(如 "123")。
    • 模式 r'^[a-zA-Z]+$' 可以匹配仅由字母组成的字符串(如 "Hello")。
  2. 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 不消耗任何字符,只匹配位置(类似于 ^ 和 $

常见匹配以下三种位置:

  1. 单词字符与非单词字符之间(如 "a b" 中的 a 和空格之间)

  2. 字符串开头与单词字符之间(如 "abc" 的开头)

  3. 单词字符与字符串结尾之间(如 "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

核心特性

  1. 起始位置匹配:只检查字符串开头是否匹配

  2. 单次匹配:只返回第一个匹配结果

  3. 非贪婪:匹配行为不受全局标志影响

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 标志。

核心特性

  1. 全局扫描:检查整个字符串而不仅是开头

  2. 单次匹配:只返回第一个匹配结果

  3. 灵活定位:可以在字符串任意位置找到匹配

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 忽略大小写)

返回值

  • 当模式没有分组时:返回所有完整匹配的字符串列表

  • 当模式有分组时:返回所有分组组成的元组列表

  • 没有匹配时:返回空列表

核心特性

  1. 全局扫描:检查整个字符串的所有匹配

  2. 返回所有结果:不止是第一个匹配

  3. 分组影响结果:有无分组返回不同结构的数据

  4. 非重叠匹配:匹配结果不会重叠

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() 返回的对象类型相同)。如果没有匹配项,迭代器将为空。

核心特点

  1. 内存高效:不会一次性返回所有结果,适合处理大文本

  2. 信息丰富:每个匹配都是完整的 Match 对象,包含位置等信息

  3. 惰性求值:只有在迭代时才会查找下一个匹配

  4. 非重叠匹配:与 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'

注意事项

  1. 贪婪匹配:默认贪婪匹配,可通过正则语法控制(如 .*?)。
  2. 特殊字符转义:若 repl 包含 $ 或 \,需用 r'' 原始字符串或双重转义。
  3. 性能优化:频繁调用时建议预编译正则表达式(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())。


网站公告

今日签到

点亮在社区的每一天
去签到