0%

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

Python 模式匹配:从 in 到正则再到 match-case,一招对一招

处理文本和数据,绕不开匹配这件事——判断一段文字里有没有某个词、提取一个手机号、解析一段 JSON 结构。

Python 提供了三个层级的工具:字符串方法(最基础)、正则表达式(最强大)、match-case 结构化匹配(Python 3.10+,专门对付数据结构)。遇到什么场景用什么工具,比学会所有工具的语法更重要。

最简单的:字符串自带的方法

能用内置方法解决的问题,就别上正则。代码短、速度快、不用记那些奇怪的符号。

1. 有没有:innot in

1
2
3
4
text = "hello world, hello python"

if "hello" in text:
print("找到了")

这是最直观的写法,没有之一。find() 也可以,但 in 更符合直觉。

2. 在哪:find()rfind()

1
2
3
text.find("hello")     # 0,第一次出现的位置
text.rfind("hello") # 13,从右边找
text.find("java") # -1,找不到返回 -1

如果你还需要知道位置,用 find()。注意它返回 -1 表示没找到,不是抛异常。

3. 开头结尾:startswith()endswith()

1
2
3
4
5
6
url.startswith("https")              # True
filename.endswith(".txt") # True

# 可以传元组,匹配任意一个
url.startswith(("http://", "https://")) # True
filename.endswith((".txt", ".md")) # True

批量处理文件的时候非常好用:

1
2
files = ["data.txt", "image.png", "notes.md"]
txt_files = [f for f in files if f.endswith(".txt")]

文件名通配符:fnmatch

如果你需要 *? 这种简单的通配符,又不想写正则,fnmatch 是折中方案。

1
2
3
4
5
6
7
8
from fnmatch import fnmatch

fnmatch("document.txt", "*.txt") # True
fnmatch("config.json", "config.*") # True

# 批量过滤
files = ["main.py", "utils.py", "test.py"]
py_files = [f for f in files if fnmatch(f, "*.py")]

fnmatch 比正则简单,比字符串方法灵活,适合文件名匹配这类场景。但仅限于 *?,复杂的就别用它了。

复杂文本:正则表达式,一劳永逸

遇到手机号、邮箱、URL、日志解析这种复杂模式,别用字符串方法硬凑,直接用正则。

常用写法速查:

需求 正则
手机号(11位) 1[3-9]\d{9}
邮箱 \w+@\w+\.\w+
数字 \d+
中文 [\u4e00-\u9fa5]+
URL https?://[^\s]+
日期(YYYY-MM-DD) \d{4}-\d{2}-\d{2}

提取手机号:

1
2
3
4
5
import re

text = "联系我:13812345678 或 13987654321"
phones = re.findall(r"1[3-9]\d{9}", text)
# ['13812345678', '13987654321']

提取邮箱 + 去重:

1
2
3
text = "test@example.com, admin@test.com, test@example.com"
emails = set(re.findall(r"\w+@\w+\.\w+", text))
# {'admin@test.com', 'test@example.com'}

提取捕获组:

1
2
3
4
5
6
7
8
log = "[2026-07-02 10:30:45] ERROR: 数据库连接超时"
pattern = r"\[(\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2})\] (\w+): (.*)"
match = re.search(pattern, log)

if match:
time = match.group(1) # "2026-07-02 10:30:45"
level = match.group(2) # "ERROR"
msg = match.group(3) # "数据库连接超时"

替换敏感词:

1
2
3
4
5
sensitive = ["垃圾", "废物", "骗子"]
pattern = re.compile("|".join(sensitive))

text = "这个产品太垃圾了,简直是废物"
pattern.sub("***", text) # "这个产品太***了,简直是***"

预编译提升性能: 如果你用同一个正则在大量文本上反复匹配,用 re.compile() 预编译能快 30%-50%。

1
2
3
4
5
6
# 先编译一次
phone_pattern = re.compile(r"1[3-9]\d{9}")

# 反复使用
for line in log_lines:
phone_pattern.findall(line)

结构化数据:Python 3.10 的 match-case

正则对付的是文本。如果你的数据已经是列表、字典、对象了,再用正则去解析就是绕远路。match-case 是专门对付数据结构的。

基础语法:

1
2
3
4
5
6
7
match 变量:
case 模式1:
# 匹配模式1
case 模式2:
# 匹配模式2
case _:
# 默认(类似 else)

匹配列表结构:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
def handle(data):
match data:
case [1, 2, x]: # 列表前两个是1和2
print(f"第三个是:{x}")
case [x, y, z]: # 任意三个元素
print(f"三个元素:{x},{y},{z}")
case [*rest]: # 任意长度
print(f"其他:{rest}")
case _:
print("不匹配")

handle([1, 2, 3]) # 第三个是:3
handle([1, 2, 4]) # 第三个是:4
handle([5, 6, 7]) # 三个元素:5,6,7

匹配字典结构:

1
2
3
4
5
6
7
8
9
10
11
12
13
def handle_response(resp):
match resp:
case {"status": 200, "body": body}:
print(f"成功:{body[:50]}...")
case {"status": 404}:
print("资源不存在")
case {"status": s} if 500 <= s < 600:
print(f"服务器错误:{s}")
case _:
print("未知响应")

handle_response({"status": 200, "body": "OK"})
handle_response({"status": 404})

匹配类对象:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
from dataclasses import dataclass

@dataclass
class Point:
x: int
y: int

def describe(p):
match p:
case Point(0, 0):
print("原点")
case Point(x, 0):
print(f"x轴上:({x}, 0)")
case Point(0, y):
print(f"y轴上:(0, {y})")
case Point(x, y):
print(f"普通点:({x}, {y})")

describe(Point(0, 0)) # 原点
describe(Point(5, 0)) # x轴上:(5, 0)

带守卫条件(if 附加判断):

1
2
3
4
case {"name": name, "age": age} if age >= 18:
print(f"成年:{name}")
case {"name": name, "age": age}:
print(f"未成年:{name}")

match-case 最擅长的是”根据数据结构的不同形状走不同分支”,而不是”判断文本内容”。数据结构越复杂,它越有用。

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