0%

Python3 模式匹配:从基础到高级的匹配技巧

Python3 模式匹配:从基础到高级的匹配技巧

模式匹配是 Python 中处理数据结构和文本的重要技术,从简单的字符串匹配到复杂的结构化数据匹配都有广泛应用。Python 3 提供了多种模式匹配工具,包括基础的字符串方法、re 模块的正则表达式,以及 Python 3.10 引入的结构化模式匹配(match-case 语句)。本文将系统介绍这些技术,帮助你高效处理各类匹配场景。

基础字符串模式匹配

Python 字符串内置了多种方法,可直接用于简单的模式匹配需求,无需引入额外模块。

1. 子串检查(包含性匹配)

  • in 运算符:检查子串是否存在于字符串中
  • str.find(sub):返回子串首次出现的索引(未找到返回 -1)
  • str.count(sub):统计子串出现的次数
1
2
3
4
5
6
7
8
9
10
11
12
13
s = "hello world, hello python"

# 检查是否包含子串
print("hello" in s) # True
print("java" in s) # False

# 查找子串位置
print(s.find("hello")) # 0(首次出现)
print(s.find("hello", 5)) # 13(从索引5开始查找)

# 统计出现次数
print(s.count("hello")) # 2
print(s.count("l")) # 5

2. 前缀 / 后缀匹配

  • str.startswith(prefix):检查字符串是否以指定前缀开头
  • str.endswith(suffix):检查字符串是否以指定后缀结尾
1
2
3
4
5
6
7
8
9
10
filename = "document.txt"
url = "https://example.com"

# 前缀匹配
print(filename.startswith("doc")) # True
print(url.startswith(("http://", "https://"))) # True(支持元组多选项)

# 后缀匹配
print(filename.endswith(".txt")) # True
print(filename.endswith((".doc", ".pdf"))) # False

正则表达式:复杂文本模式匹配

对于更复杂的模式(如邮箱验证、手机号提取、格式化字符串解析等),需要使用 Python 内置的 re 模块(正则表达式)。正则表达式通过 “模式字符串” 定义匹配规则,支持通配符、重复、分组等高级特性。

1. 正则表达式基础语法

元字符 描述 示例
. 匹配任意单个字符(除换行符) a.b 匹配 “aab”、”acb” 等
* 匹配前一个字符 0 次或多次 ab* 匹配 “a”、”ab”、”abb” 等
+ 匹配前一个字符 1 次或多次 ab+ 匹配 “ab”、”abb” 等(不匹配 “a”)
? 匹配前一个字符 0 次或 1 次 ab? 匹配 “a”、”ab”
^ 匹配字符串开头 ^hello 匹配以 “hello” 开头的字符串
$$` 匹配字符串结尾 `world$$ 匹配以 “world” 结尾的字符串
[] 匹配括号内的任意一个字符 [abc] 匹配 “a”、”b” 或 “c”
[^] 匹配不在括号内的任意字符 [^abc] 匹配除 “a”、”b”、”c” 外的字符
() 分组,将部分模式视为一个整体 (ab)+ 匹配 “ab”、”abab” 等
\d 匹配任意数字(等价于 [0-9] \d{3} 匹配 3 位数字
\D 匹配非数字(等价于 [^0-9]
\w 匹配字母、数字或下划线(等价于 [a-zA-Z0-9_] \w+ 匹配单词
\W 匹配非单词字符
\s 匹配空白字符(空格、制表符、换行符等)
\S 匹配非空白字符
{n} 匹配前一个字符恰好 n 次 a{3} 匹配 “aaa”
{n,} 匹配前一个字符至少 n 次 a{2,} 匹配 “aa”、”aaa” 等
{n,m} 匹配前一个字符 n 到 m 次 a{1,3} 匹配 “a”、”aa”、”aaa”

2. re 模块核心函数

函数 描述
re.match(pattern, string) 从字符串开头匹配模式(仅匹配开头)
re.search(pattern, string) 在整个字符串中搜索第一个匹配项
re.findall(pattern, string) 查找所有匹配项,返回列表
re.finditer(pattern, string) 查找所有匹配项,返回迭代器(含匹配对象)
re.sub(pattern, repl, string) 替换所有匹配项为 repl
re.split(pattern, string) 按匹配项分割字符串

3. 常用正则匹配场景示例

场景 1:验证邮箱格式
1
2
3
4
5
6
7
8
9
10
import re

def is_valid_email(email):
# 简单邮箱正则:用户名@域名.后缀(域名至少1个字符,后缀至少2个字符)
pattern = r"^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+$"
return re.match(pattern, email) is not None

print(is_valid_email("test@example.com")) # True
print(is_valid_email("invalid-email.com")) # False
print(is_valid_email("user.name+tag@domain.co.uk")) # True
场景 2:提取文本中的所有手机号
1
2
3
4
5
6
7
8
import re

text = "联系我们:13812345678,或拨打400-800-8888,也可联系13987654321"

# 匹配手机号:11位数字,或带分隔符的400电话
pattern = r"1\d{10}|400-\d{3}-\d{4}"
phones = re.findall(pattern, text)
print(phones) # ['13812345678', '400-800-8888', '13987654321']
场景 3:替换文本中的敏感词
1
2
3
4
5
6
7
8
9
10
11
import re

text = "这个产品太垃圾了,简直是废物!"
sensitive_words = ["垃圾", "废物"]

# 构建敏感词正则(用|分隔多个词,re.IGNORECASE忽略大小写)
pattern = re.compile(r"|".join(sensitive_words), re.IGNORECASE)

# 替换敏感词为***
censored_text = pattern.sub("***", text)
print(censored_text) # 这个产品太***了,简直是***!
场景 4:解析 URL 中的参数
1
2
3
4
5
6
7
8
import re

url = "https://example.com/search?query=python&page=2&sort=desc"

# 匹配 URL 参数(key=value 形式)
pattern = r"(\w+)=(\w+)"
params = re.findall(pattern, url)
print(dict(params)) # {'query': 'python', 'page': '2', 'sort': 'desc'}

结构化模式匹配(Python 3.10+)

Python 3.10 引入了 match-case 语句,支持对数据结构(如列表、元组、字典、对象)进行模式匹配,类似于其他语言的 switch-case,但功能更强大。

1. 基础语法

1
2
3
4
5
6
7
match 变量:
case 模式1:
# 匹配模式1时执行
case 模式2:
# 匹配模式2时执行
case _:
# 所有模式都不匹配时执行(默认分支,类似 else)

2. 常见结构化匹配场景

场景 1:匹配字面量和变量
1
2
3
4
5
6
7
8
9
10
11
12
13
def handle_command(command):
match command:
case "start":
print("启动程序")
case "stop":
print("停止程序")
case "restart":
print("重启程序")
case other: # 变量捕获:匹配任意值并赋值给 other
print(f"未知命令:{other}")

handle_command("start") # 启动程序
handle_command("pause") # 未知命令:pause
场景 2:匹配序列(列表 / 元组)
1
2
3
4
5
6
7
8
9
10
11
12
def process_data(data):
match data:
case [1, 2, x]: # 匹配长度为3,前两个元素为1、2的列表
print(f"匹配成功,第三个元素是:{x}")
case ( "user", name, age ): # 匹配长度为3,第一个元素为"user"的元组
print(f"用户信息:姓名={name},年龄={age}")
case _:
print("未匹配的结构")

process_data([1, 2, 3]) # 匹配成功,第三个元素是:3
process_data(("user", "Alice", 25)) # 用户信息:姓名=Alice,年龄=25
process_data([1, 3, 5]) # 未匹配的结构
场景 3:匹配字典(键值对)
1
2
3
4
5
6
7
8
9
10
11
def handle_user(user):
match user:
case {"name": name, "age": age} if age >= 18: # 带条件的字典匹配
print(f"成年用户:{name}{age}岁)")
case {"name": name, "age": age}: # 匹配包含name和age的字典
print(f"未成年用户:{name}{age}岁)")
case _:
print("无效的用户数据")

handle_user({"name": "Bob", "age": 20}) # 成年用户:Bob(20岁)
handle_user({"name": "Charlie", "age": 15}) # 未成年用户:Charlie(15岁)
场景 4:匹配类对象
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class Point:
def __init__(self, x, y):
self.x = x
self.y = y

def describe_point(point):
match point:
case Point(0, 0): # 匹配原点
print("这是原点")
case Point(x, 0): # 匹配x轴上的点
print(f"这是x轴上的点:x={x}")
case Point(0, y): # 匹配y轴上的点
print(f"这是y轴上的点:y={y}")
case Point(x, y): # 匹配其他点
print(f"这是普通点:({x}, {y})")

describe_point(Point(0, 0)) # 这是原点
describe_point(Point(5, 0)) # 这是x轴上的点:x=5
describe_point(Point(3, 4)) # 这是普通点:(3, 4)
场景 5:组合模式与通配符
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
def parse_expression(expr):
match expr:
case ["add", a, b]: # 匹配加法表达式
return a + b
case ["multiply", a, b]: # 匹配乘法表达式
return a * b
case ["power", base, 2]: # 匹配平方运算
return base **2
case ["power", base, exp]: # 匹配其他幂运算
return base** exp
case _:
raise ValueError("未知表达式")

print(parse_expression(["add", 2, 3])) # 5
print(parse_expression(["multiply", 4, 5])) # 20
print(parse_expression(["power", 3, 2])) # 9
print(parse_expression(["power", 2, 5])) # 32

三种模式匹配技术的对比与选择

技术 适用场景 优势 局限性
字符串内置方法 简单子串检查、前缀 / 后缀匹配 无需额外模块,直观高效 不支持复杂模式(如通配符、重复)
正则表达式(re 模块) 复杂文本匹配(邮箱、手机号、日志解析等) 支持强大的模式定义,适合文本处理 语法较复杂,性能对复杂模式可能较差
结构化模式匹配(match-case 数据结构匹配(列表、字典、对象等) 支持结构化数据,语法清晰,支持条件匹配 仅 Python 3.10+ 支持,不适合文本匹配

实战案例:日志分析工具

结合正则表达式和字符串方法,实现一个简单的日志分析工具,提取错误日志并统计频率:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
import re
from collections import defaultdict

def analyze_error_logs(log_text):
# 正则:匹配错误日志(格式:[时间] ERROR: 消息)
error_pattern = r"\[(\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2})\] ERROR: (.*)"

# 查找所有错误日志
errors = re.findall(error_pattern, log_text)

if not errors:
print("未发现错误日志")
return

# 统计错误消息频率
error_counts = defaultdict(int)
for time, msg in errors:
error_counts[msg] += 1

# 输出结果
print(f"共发现 {len(errors)} 条错误日志:")
for msg, count in error_counts.items():
print(f"- {msg}(出现 {count} 次)")

# 示例日志文本
log_text = """
[2023-10-01 08:30:00] INFO: 程序启动
[2023-10-01 08:30:05] ERROR: 数据库连接失败
[2023-10-01 08:30:10] ERROR: 数据库连接失败
[2023-10-01 08:30:15] INFO: 重试连接
[2023-10-01 08:30:20] ERROR: 权限不足
"""

analyze_error_logs(log_text)

运行结果

1
2
3
共发现 3 条错误日志:
- 数据库连接失败(出现 2 次)
- 权限不足(出现 1 次)

欢迎关注我的其它发布渠道

表情 | 预览
快来做第一个评论的人吧~
Powered By Valine
v1.3.10