Python 模式匹配:从 in 到正则再到 match-case,一招对一招 处理文本和数据,绕不开匹配这件事——判断一段文字里有没有某个词、提取一个手机号、解析一段 JSON 结构。
Python 提供了三个层级的工具:字符串方法 (最基础)、正则表达式 (最强大)、match-case 结构化匹配 (Python 3.10+,专门对付数据结构)。遇到什么场景用什么工具,比学会所有工具的语法更重要。
最简单的:字符串自带的方法 能用内置方法解决的问题,就别上正则。代码短、速度快、不用记那些奇怪的符号。
1. 有没有:in 和 not 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" ) text.rfind("hello" ) text.find("java" )
如果你还需要知道位置,用 find()。注意它返回 -1 表示没找到,不是抛异常。
3. 开头结尾:startswith() 和 endswith() 1 2 3 4 5 6 url.startswith("https" ) filename.endswith(".txt" ) url.startswith(("http://" , "https://" )) filename.endswith((".txt" , ".md" ))
批量处理文件的时候非常好用:
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 fnmatchfnmatch("document.txt" , "*.txt" ) fnmatch("config.json" , "config.*" ) 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 retext = "联系我:13812345678 或 13987654321" phones = re.findall(r"1[3-9]\d{9}" , text)
提取邮箱 + 去重: 1 2 3 text = "test@example.com, admin@test.com, test@example.com" emails = set (re.findall(r"\w+@\w+\.\w+" , text))
提取捕获组: 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 ) level = match .group(2 ) 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 : case 模式2 : case _:
匹配列表结构: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 def handle (data ): match data: case [1 , 2 , x]: print (f"第三个是:{x} " ) case [x, y, z]: print (f"三个元素:{x} ,{y} ,{z} " ) case [*rest]: print (f"其他:{rest} " ) case _: print ("不匹配" ) handle([1 , 2 , 3 ]) handle([1 , 2 , 4 ]) handle([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 ))
带守卫条件(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 最擅长的是”根据数据结构的不同形状走不同分支”,而不是”判断文本内容”。数据结构越复杂,它越有用。