Python 文件通配符搜索深度指南:glob, fnmatch, pathlib, re 全面解析230


在日常的软件开发和系统管理中,我们经常需要根据特定的模式来查找文件。无论是清理旧日志、处理用户上传的图片,还是自动化构建过程,高效地定位文件都是一项核心任务。Python 作为一种功能强大且易于使用的编程语言,提供了多种模块来应对文件通配符搜索的需求。本文将作为一份深度指南,详细解析 Python 中用于文件通配符搜索的各种工具,包括 glob、fnmatch、 结合 fnmatch、现代的 pathlib 以及更高级的正则表达式 re 模块,帮助您根据具体场景选择最合适的解决方案。

什么是通配符?为何使用它?

通配符(Wildcard)是一种特殊字符,用于在文件或目录名中替代一个或多个字符。它们提供了一种灵活且简洁的方式来匹配文件,而无需指定完整的精确名称。最常见的通配符包括:
*:匹配零个或多个任意字符。例如,*.txt 匹配所有以 .txt 结尾的文件,data_* 匹配所有以 data_ 开头的文件。
?:匹配一个任意字符。例如,file_?.log 匹配 、 等,但不匹配 。
[]:匹配括号内任意一个字符。例如,[abc].txt 匹配 、、。可以指定范围,如 [0-9].log 匹配 到 。

使用通配符的好处在于:
效率: 快速定位所需文件,无需手动逐一检查。
灵活性: 应对文件命名规则的变化。
自动化: 便于编写脚本自动化文件管理任务。

一、`glob` 模块:简单而直接的文件匹配

glob 模块是 Python 中进行文件路径名模式匹配最常用和最直接的工具,它遵循 Unix shell 的通配符规则。如果您只需要在单个目录或其子目录中查找匹配特定模式的文件,glob 通常是您的首选。

基本用法


(pathname, *, recursive=False) 函数返回所有匹配 pathname 的文件或目录的列表。pathname 可以包含标准的 Unix shell 风格通配符。

示例 1:查找当前目录下所有 .txt 文件import glob
import os
# 创建一些示例文件
('data', exist_ok=True)
with open('data/', 'w') as f: ('...')
with open('data/', 'w') as f: ('...')
with open('data/', 'w') as f: ('...')
with open('data/', 'w') as f: ('...')
with open('data/', 'w') as f: ('...')
print("当前目录下的所有 .txt 文件:")
txt_files = ('data/*.txt')
for f in txt_files:
print(f)
# 预期输出:
# data/
# data/

示例 2:使用 `?` 匹配单个字符print("匹配 'data/file_?.log' 模式的文件:")
# 创建一个符合模式的示例文件
with open('data/', 'w') as f: ('...')
with open('data/', 'w') as f: ('...')
with open('data/', 'w') as f: ('...') # 不会被 '?' 匹配
log_files = ('data/file_?.log')
for f in log_files:
print(f)
# 预期输出:
# data/
# data/

示例 3:使用 `[]` 匹配字符集或范围print("匹配 'data/report_[0-9].txt' 模式的文件:")
# 创建一个符合模式的示例文件
with open('data/', 'w') as f: ('...')
with open('data/', 'w') as f: ('...') # 不会被 '[0-9]' 匹配
specific_reports = ('data/report_[0-9].txt')
for f in specific_reports:
print(f)
# 预期输出:
# data/

递归搜索 (`recursive=True`)


从 Python 3.5 开始,() 和 () 支持 recursive=True 参数,允许使用 来匹配任意深度的目录和文件。这使得递归搜索变得异常简单。

示例 4:递归查找所有子目录中的 .log 文件import glob
import os
# 创建多层嵌套目录和文件
('logs/2023/q1', exist_ok=True)
('logs/2024/q2', exist_ok=True)
with open('logs/', 'w') as f: ('...')
with open('logs/2023/q1/', 'w') as f: ('...')
with open('logs/2024/q2/', 'w') as f: ('...')
with open('logs/2024/q2/', 'w') as f: ('...')
print("递归查找 'logs/' 目录下所有 .log 文件:")
all_log_files = ('logs//*.log', recursive=True)
for f in all_log_files:
print(f)
# 预期输出 (顺序可能不同):
# logs/
# logs/2023/q1/
# logs/2024/q2/

内存优化:`()`


当处理大量文件时,() 会一次性将所有匹配的文件路径加载到内存中,这可能导致内存问题。() 返回一个迭代器,允许您逐个处理文件路径,从而节省内存。print("使用 iglob 迭代器查找所有文件 (内存高效):")
for f in ('logs//*.log', recursive=True):
print(f"处理文件: {f}")

二、`fnmatch` 模块:针对单个文件名的匹配

fnmatch 模块提供了对 Unix shell 风格通配符的支持,但它的主要用途是针对单个文件名字符串进行匹配,而不是像 glob 那样生成文件列表。它通常与 () 或 () 结合使用,以在遍历文件时筛选文件名。

基本用法



(filename, pattern):检查 filename 是否匹配 pattern。默认是平台相关的,即在大小写敏感的文件系统上区分大小写,否则不区分。
(filename, pattern):与 fnmatch 类似,但始终区分大小写。
(names, pattern):从 names 列表中筛选出匹配 pattern 的文件名。

示例 5:使用 `fnmatch` 匹配单个文件名import fnmatch
print("fnmatch 模块示例:")
print(f"'' 匹配 '*.txt'? {('', '*.txt')}")
print(f"'' 匹配 '*.txt'? {('', '*.txt')}") # 视系统而定可能为True或False
print(f"'' 匹配 '*.txt' (区分大小写)? {('', '*.txt')}") # 始终为False
print(f"'' 匹配 'file_?.log'? {('', 'file_?.log')}")
print(f"'' 匹配 'doc*.pdf'? {('', 'doc*.pdf')}") # False, 因为'doc'不是开头

结合 `()` 进行目录内筛选


() 在需要从现有文件列表中筛选文件时非常有用。import os
import fnmatch
# 假设 'data' 目录下有这些文件
# , , , , , , , ,
# (这些文件已在前面示例中创建)
print("结合 () 和 ():")
all_data_files = ('data')
print(f"data 目录下所有文件: {all_data_files}")
filtered_txt_files = (all_data_files, '*.txt')
print(f"过滤后的 .txt 文件: {filtered_txt_files}")
filtered_report_files = (all_data_files, 'report_*.txt')
print(f"过滤后的 report_*.txt 文件: {filtered_report_files}")

三、`` + `fnmatch`:强大的递归搜索组合

当需要更精细地控制文件遍历过程,并且在遍历过程中对每个文件或目录执行操作时,() 是一个非常强大的工具。结合 fnmatch,它可以实现复杂的递归文件搜索逻辑。

(top, topdown=True, onerror=None, followlinks=False) 生成器会递归地遍历 top 目录下的所有子目录和文件。对于每一个目录,它会返回一个三元组 (dirpath, dirnames, filenames):
dirpath:当前正在遍历的目录的路径。
dirnames:当前目录下的子目录名称列表。
filenames:当前目录下的文件名称列表。

示例 6:递归查找特定模式的文件import os
import fnmatch
search_root = 'logs'
pattern = '*.log'
found_files = []
print(f"使用 和 fnmatch 递归查找 '{search_root}' 目录下的 '{pattern}' 文件:")
for root, _, files in (search_root):
for filename in (files, pattern):
((root, filename))
for f in found_files:
print(f)
# 预期输出与 ('logs//*.log', recursive=True) 相同

这种组合的优势在于,您可以在 循环中执行除了匹配之外的更多操作,例如检查文件大小、修改时间,或者根据其他条件跳过某些目录。

四、`pathlib` 模块:现代、面向对象的路径操作

pathlib 模块是 Python 3.4 引入的,它以面向对象的方式处理文件系统路径,提供了更清晰、更直观的 API。它大大简化了路径拼接、检查文件类型、访问文件元数据等操作,并且内置了对通配符搜索的支持。

基本用法


Path 对象是 pathlib 的核心。您可以创建 Path 对象,然后使用它的 glob() 或 rglob() 方法进行通配符搜索。
(pattern):在当前 Path 对象代表的目录下查找匹配 pattern 的文件,不递归。
(pattern):递归查找当前 Path 对象代表的目录及其子目录中匹配 pattern 的文件。

示例 7:使用 `()` 和 `()`from pathlib import Path
# 获取当前工作目录的 Path 对象
current_dir = Path('.')
# 查找 'data' 目录下所有 .txt 文件 (不递归)
print("pathlib: 查找 'data' 目录下所有 .txt 文件 (非递归):")
for f in ('data/*.txt'):
print(f)
# 递归查找 'logs' 目录下所有 .log 文件
print("pathlib: 递归查找 'logs' 目录下所有 .log 文件:")
for f in ('logs//*.log'): # 或者直接 Path('logs').rglob('*.log')
print(f)
# 访问文件属性 (Pathlib 的额外优势)
first_log_file = next(('logs//*.log'), None)
if first_log_file:
print(f"第一个日志文件: {first_log_file}")
print(f"文件名: {}")
print(f"文件后缀: {}")
print(f"父目录: {}")
print(f"是否存在: {()}")
print(f"是否是文件: {first_log_file.is_file()}")
print(f"是否是目录: {first_log_file.is_dir()}")

pathlib 的优势在于其直观的 API 和链式调用能力,让代码更具可读性和可维护性。例如,您可以轻松地获取文件的父目录、扩展名等。

五、`re` 模块:超越通配符的正则表达式匹配

当文件名的匹配模式变得非常复杂,超出了简单通配符(*, ?, [])的能力范围时,正则表达式(Regular Expressions, regex)就显得不可或缺了。Python 的 re 模块提供了完整的正则表达式功能。

正则表达式允许您定义极其精细的匹配规则,例如:
匹配以特定字符开头和结尾的文件。
匹配包含特定数字序列或字母组合的文件。
匹配文件名中特定位置的日期格式。

与文件搜索结合


由于 re 模块本身不提供文件系统遍历功能,它通常与 () 或 () 结合使用。

示例 8:使用正则表达式查找特定模式的文件import os
import re
search_root = 'data'
# 匹配 "report_" 开头,后面跟着四位数字,以 ".txt" 结尾的文件
# 例如: ,
pattern = r'^report_\d{4}\.txt$' # \d 匹配数字,{4} 匹配四次,^表示开头,$表示结尾
found_files = []
print(f"使用 re 模块查找 '{search_root}' 目录下符合正则表达式 '{pattern}' 的文件:")
for filename in (search_root):
if (pattern, filename):
((search_root, filename))
for f in found_files:
print(f)
# 预期输出:
# data/

示例 9:结合 `` 和 `re` 进行递归复杂搜索import os
import re
search_root = 'logs'
# 匹配 "error_" 开头,后面跟着任意字符,最后以 ".log" 结尾的文件
# 例如: ,
pattern = r'^error_.*\.log$'
recursive_found_files = []
print(f"结合 和 re 递归查找 '{search_root}' 目录下符合正则表达式 '{pattern}' 的文件:")
for root, _, files in (search_root):
for filename in files:
if (pattern, filename):
((root, filename))
# 创建一个符合模式的示例文件
with open('logs/', 'w') as f: ('...')
with open('logs/2023/q1/', 'w') as f: ('...')
for f in recursive_found_files:
print(f)
# 预期输出 (可能包含):
# logs/
# logs/2023/q1/

虽然正则表达式提供了强大的匹配能力,但它的学习曲线相对陡峭,且对于简单的通配符匹配来说,可能会引入不必要的复杂性。因此,应根据实际需求权衡选择。

六、性能与最佳实践
选择合适的工具:

对于简单的非递归搜索,() 是最简洁高效的。
对于简单的递归搜索(Python 3.5+),(..., recursive=True) 或 () 是最佳选择。
如果需要更精细的控制,例如在遍历时过滤目录或获取文件元数据,() 结合 fnmatch 或 re 是灵活的方案。
如果追求代码的现代性、可读性和面向对象风格,优先考虑 pathlib。
只有当通配符不足以表达您的匹配逻辑时,才使用正则表达式 re。


内存管理:

当处理大量文件时,优先使用迭代器(如 () 或 ()),而不是一次性返回所有结果的列表函数(如 ()),以避免内存溢出。
() 本身也是一个生成器,逐个返回目录信息,因此在处理大量文件时也很高效。


错误处理:

在实际应用中,文件系统操作可能会遇到权限问题、文件不存在等异常。考虑使用 try-except 块来处理 PermissionError 或 FileNotFoundError。


路径处理:

始终使用 ()(或 pathlib 的 / 运算符)来拼接路径,以确保跨平台兼容性。
注意相对路径和绝对路径的使用。如果脚本会从不同位置运行,考虑使用绝对路径或基于脚本位置的相对路径。


清晰命名:

为通配符模式和正则表达式模式使用清晰的变量名,并添加注释,以提高代码可读性。




Python 提供了多种强大的工具来处理文件通配符搜索,从简单的 glob 到现代的 pathlib,再到强大的 re 模块。每种工具都有其独特的优势和适用场景。掌握这些工具,并根据您的具体需求和文件复杂度进行选择,将显著提高您在文件系统操作方面的效率和代码质量。

通过本文的深度解析,相信您现在已经对如何在 Python 中有效地利用通配符和正则表达式搜索文件有了全面的理解,并能在未来的项目中做出明智的技术选型。

2026-04-03


上一篇:Python代码组织与风格指南:提升可读性、可维护性和协作效率

下一篇:Python高效统计TXT文件字符串:词频、字符与模式分析实战