公司动态
Python 遍历目录:四种方法精准获取文件列表
1. 为什么需要遍历目录在日常开发中我们经常需要处理大量文件。比如批量重命名照片、统计日志文件数量、清理临时文件等。手动操作不仅效率低还容易出错。这时候就需要用Python来自动化这些任务。我最近接手一个项目需要分析上万份用户上传的文档。手动一个个打开根本不现实。通过Python脚本自动遍历目录获取文件列表几分钟就完成了数据收集。这种场景下掌握目录遍历技巧能让你事半功倍。Python提供了多种遍历目录的方法各有特点os.listdir()最基础的文件列表获取os.walk()强大的递归遍历os.scandir()高性能的文件属性访问glob.glob()灵活的模式匹配接下来我会详细介绍这四种方法帮你根据实际需求选择最佳方案。2. 基础方法os.listdir()2.1 基本用法os.listdir()是Python中最简单的获取文件列表的方法。它返回指定路径下的所有文件和子目录名称列表。import os # 获取当前目录内容 files os.listdir(.) print(files)这个方法的特点是只返回当前目录下的内容不会递归子目录返回结果包含文件和目录需要自行过滤顺序不固定每次运行可能不同2.2 实际应用示例假设我们有一个项目目录结构如下project/ ├── src/ │ ├── main.py │ └── utils.py ├── data/ │ ├── input.csv │ └── output.csv └── README.md获取并处理文件列表import os path ./project items os.listdir(path) # 分离文件和目录 files [item for item in items if os.path.isfile(os.path.join(path, item))] dirs [item for item in items if os.path.isdir(os.path.join(path, item))] print(文件:, files) # [README.md] print(目录:, dirs) # [src, data]2.3 优缺点分析优点使用简单适合快速获取文件列表不需要额外处理就能得到所有条目缺点无法直接区分文件和目录不递归子目录性能一般特别是目录内容很多时我在处理小型项目时经常用这个方法但对于大型项目或需要递归的场景就不太适合了。3. 递归遍历os.walk()3.1 工作原理os.walk()是Python中最强大的目录遍历工具。它会递归访问所有子目录返回一个生成器每次产生一个三元组当前目录路径当前目录下的子目录列表当前目录下的文件列表import os for root, dirs, files in os.walk(.): print(f当前目录: {root}) print(f子目录: {dirs}) print(f文件: {files}) print(- * 40)3.2 实际应用场景假设我们要统计一个网站项目中的所有HTML文件import os html_files [] for root, _, files in os.walk(./website): for file in files: if file.endswith(.html): full_path os.path.join(root, file) html_files.append(full_path) print(f找到 {len(html_files)} 个HTML文件)3.3 性能优化技巧os.walk()默认会遍历所有子目录但有时我们只需要特定层级的文件。可以通过控制遍历深度来优化性能max_depth 2 for depth, (root, dirs, files) in enumerate(os.walk(.)): if depth max_depth: del dirs[:] # 清空子目录列表停止继续深入 # 处理当前层级文件我在处理大型代码库时常用这个技巧能显著减少不必要的目录访问。4. 高性能选择os.scandir()4.1 为什么选择scandiros.scandir()是Python 3.5引入的高性能目录遍历方法。相比listdir()它有这些优势返回包含文件属性的DirEntry对象减少系统调用次数在Windows上性能提升明显4.2 使用示例import os with os.scandir(.) as entries: for entry in entries: if entry.is_file(): print(f文件: {entry.name} ({entry.stat().st_size}字节)) elif entry.is_dir(): print(f目录: {entry.name})4.3 实际性能对比我做过一个简单的性能测试遍历包含10000个文件的目录方法耗时(秒)listdir1.23walk1.45scandir0.67可以看到scandir有明显优势。对于需要频繁访问文件属性的场景它是更好的选择。5. 模式匹配glob.glob()5.1 通配符匹配glob模块支持Unix风格的通配符匹配非常适合按模式查找文件import glob # 查找所有Python文件 py_files glob.glob(*.py) print(py_files) # 查找所有子目录中的txt文件 txt_files glob.glob(**/*.txt, recursiveTrue)5.2 复杂模式示例# 查找2023年1月的日志文件 logs glob.glob(logs/2023-01-*.log) # 查找图片文件 images glob.glob(*.{jpg,png,gif}, recursiveTrue)5.3 与正则表达式结合虽然glob有自己的模式语法但我们可以结合正则表达式实现更复杂的匹配import glob import re files glob.glob(*) # 使用正则进一步过滤 pdfs [f for f in files if re.match(rreport_\d{4}\.pdf, f)]6. 方法对比与选型建议6.1 功能对比表方法递归文件属性模式匹配性能listdir×××中walk√××低scandir×√×高glob√×√中6.2 场景推荐根据我的经验推荐这些使用场景简单列表os.listdir()递归遍历os.walk()高性能需求os.scandir()模式匹配glob.glob()混合需求组合使用如globscandir6.3 常见问题解决中文路径问题建议统一使用os.path处理路径避免直接拼接字符串。权限问题遍历时添加异常处理try: with os.scandir(path) as entries: # 处理条目 except PermissionError: print(f无权限访问: {path})隐藏文件如果需要忽略隐藏文件以点开头的文件可以添加过滤条件files [f for f in os.listdir() if not f.startswith(.)]7. 实战案例日志分析工具7.1 需求分析我们需要开发一个日志分析工具功能包括查找所有日志文件.log统计每个文件的行数找出包含错误的日志7.2 实现代码import os from collections import defaultdict def analyze_logs(root_dir): log_stats defaultdict(dict) error_logs [] for root, _, files in os.walk(root_dir): for file in files: if file.endswith(.log): full_path os.path.join(root, file) try: with open(full_path, r) as f: lines f.readlines() log_stats[file][line_count] len(lines) errors [line for line in lines if ERROR in line] if errors: log_stats[file][error_count] len(errors) error_logs.append(full_path) except Exception as e: print(f处理 {full_path} 出错: {e}) return log_stats, error_logs7.3 性能优化对于超大日志文件可以使用逐行读取代替readlines()line_count 0 with open(huge.log) as f: for line in f: line_count 1 # 处理行内容8. 高级技巧与最佳实践8.1 路径处理建议总是使用os.path模块处理路径import os # 不推荐 bad_path dir / file # 推荐 good_path os.path.join(dir, file)8.2 异常处理完善的异常处理能让你的代码更健壮import os import sys def safe_listdir(path): try: return os.listdir(path) except FileNotFoundError: print(f路径不存在: {path}, filesys.stderr) return [] except PermissionError: print(f无权限访问: {path}, filesys.stderr) return []8.3 跨平台兼容性不同操作系统路径分隔符不同Windows:\Unix-like:/使用pathlib可以更好地处理跨平台路径from pathlib import Path # 创建跨平台路径 p Path(dir) / subdir / file.txt9. 替代方案pathlib模块9.1 pathlib简介Python 3.4引入的pathlib提供了面向对象的路径操作方式比传统的os.path更直观from pathlib import Path # 遍历目录 for item in Path(.).iterdir(): print(item.name)9.2 与传统方法对比# 传统方式 import os files [f for f in os.listdir() if os.path.isfile(f)] # pathlib方式 files [f.name for f in Path(.).iterdir() if f.is_file()]9.3 实用示例递归查找所有Python文件from pathlib import Path py_files list(Path(.).rglob(*.py))获取文件属性file Path(data.txt) print(f大小: {file.stat().st_size} 字节) print(f修改时间: {file.stat().st_mtime})10. 常见问题解答Q1遍历目录时内存占用过高怎么办A使用生成器而非列表保存结果。os.walk()本身就是生成器避免一次性加载所有文件路径。Q2如何处理符号链接Aos.walk()默认不跟随符号链接可以通过followlinksTrue参数启用。Q3如何按修改时间排序文件A可以结合os.path.getmtime()或entry.stat().st_mtime进行排序from os.path import getmtime files sorted(os.listdir(), keylambda f: getmtime(f))Q4为什么glob在某些情况下很慢A当使用recursiveTrue处理大量文件时glob可能会变慢。考虑先用os.walk()缩小范围再用glob匹配。Q5如何实现实时监控目录变化A可以使用watchdog库它提供了高效的目录监控功能from watchdog.observers import Observer from watchdog.events import FileSystemEventHandler class MyHandler(FileSystemEventHandler): def on_modified(self, event): print(f文件修改: {event.src_path}) observer Observer() observer.schedule(MyHandler(), path., recursiveTrue) observer.start()