fnmatch 默认大小写行为因平台而异:Windows/macOS不区分,Linux区分;可靠做法是显式使用fnmatchcase()进行严格字面匹配。

fnmatch 为什么匹配不到文件名?大小写敏感是默认行为
Python 的 fnmatch 模块默认按平台规则处理大小写:在 Windows 和 macOS 上不区分大小写,在 Linux 上区分。但这个“平台相关”行为其实不可靠——它依赖于 os.path.normcase() 的实现,而该函数对路径字符串做标准化时,并不真正执行文件系统检查。结果就是:你在 Linux 上用 fnmatch.fnmatch('Readme.md', '*.MD') 会返回 False,哪怕文件存在且扩展名确实是大写。
真正可控的方式是显式使用 fnmatch.fnmatchcase(),它完全忽略平台,严格按字面大小写匹配:
import fnmatch
fnmatch.fnmatchcase('Readme.md', '*.MD') # False
fnmatch.fnmatchcase('Readme.MD', '*.MD') # True常见误用场景:
- 写自动化脚本批量处理日志文件,用
'*.LOG'去匹配 Linux 下生成的app.log,结果全漏掉 - 打包工具中判断资源文件类型,硬编码
'*.PNG'却收到小写的icon.png
fnmatch 和 glob 的核心区别在哪?别把通配符当正则用
fnmatch 只支持 shell 风格通配符:*(任意字符)、?(单个字符)、[abc](字符类),不支持 +、d、^ 等正则语法。很多人试图写 fnmatch.fnmatch('file123.txt', 'filed+.txt'),结果永远是 False —— 因为 d 被当作字面量 d 处理。
立即学习“Python免费学习笔记(深入)”;
如果你需要正则能力,直接换 re.match();如果只是文件名过滤,坚持用 fnmatch 更轻量、更安全:
import fnmatch, re
<h1>✅ 正确:shell 风格通配</h1><p>fnmatch.fnmatch('test<em>v2.py', 'test</em>*.py') # True</p><h1>❌ 错误:混用正则语法</h1><p>fnmatch.fnmatch('test_v2.py', 'test_v.*.py') # 实际匹配 'test_v.py',不是正则!</p><h1>✅ 改用 re(需注意转义)</h1><p>re.match(r'test_vd+.py', 'test_v2.py') # True另外注意:glob.glob() 内部调用 fnmatch,所以它的模式规则和 fnmatch 完全一致,只是多了路径展开能力。
如何安全地批量匹配多个后缀?避免写死一堆 or 条件
想匹配 .py、.pyw、.pyi,别这么写:
图片提示词生成器?不止如此。 马甲系统 —— 把脑海中的画面,翻译成AI能理解的专业表达。 用得越多,它越懂你:首次需要多问几句确认方向,用久了几乎一说就懂。 用得越多,它越快:缓存机制让后续对话越来越省。 RAG进化:成功案例持续入库,越跑越聪明。 输入「新手指南」查看完整功能介绍
if name.endswith('.py') or name.endswith('.pyw') or name.endswith('.pyi'):更清晰、可维护的方式是用 fnmatch 配合元组遍历,或用字符类(当后缀有规律时):
import fnmatch <p>name = 'script.pyw'</p><h1>✅ 方式一:用字符类(适合后缀仅末位不同)</h1><p>fnmatch.fnmatchcase(name, '*.py[wi]') # True</p><h1>✅ 方式二:枚举模式(适合无规律后缀)</h1><p>patterns = ['<em>.py', '</em>.pyw', '*.pyi'] any(fnmatch.fnmatchcase(name, pat) for pat in patterns) # True</p><h1>⚠️ 注意:不要用 fnmatch.filter() 对纯文件名列表——它设计用于已知路径列表的筛选,不是通用匹配器
性能提示:如果要高频匹配(如扫描数万文件),预编译模式没用(fnmatch 无 compile 接口),但把 patterns 提前定义为 tuple 或 frozenset 可减少重复构造开销。
Windows 路径分隔符在 fnmatch 中怎么处理?统一用 / 或 os.sep
fnmatch 本身不解析路径,只做字符串匹配。当你传入 'src\utils\*.py' 这样的 Windows 路径,而模式是 '*/utils/*.py',两者因分隔符不一致必然不匹配。
解决方案只有两个,且必须显式做:
- 统一转成正斜杠:
path.replace('\', '/'),再用/写模式 - 用
os.sep动态构造模式,例如f'*{os.sep}utils{os.sep}*.py'
不要依赖 os.path.normpath(),它不会把 \ 转成 /,在 Windows 上仍返回 \ 分隔的字符串。
典型错误:
# ❌ 在 Windows 上运行失败
import fnmatch, os
fnmatch.fnmatch('src\main.py', 'src/*.py') # False —— 因为 '\' ≠ '/'实际项目中,建议所有通配模式都用 / 编写,输入路径先标准化:
path = path.replace('\', '/')
fnmatch.fnmatchcase(path, 'src/*.py')这能保证跨平台行为一致,也和大多数文档、配置项中的路径习惯对齐。

















