☰
Python文件夹遍历技巧:从基础到高级实践
2026/10/14 12:14:21 网站建设 项目流程

1. 为什么需要遍历文件夹?

在日常开发中,处理文件系统是Python程序员经常遇到的任务。无论是批量重命名文件、统计文件数量、搜索特定文件,还是进行数据分析前的数据收集,都需要先获取目标文件夹下的所有文件列表。

我遇到过很多开发者还在手动记录文件路径,或者只会处理单层目录。实际上Python提供了多种高效的文件遍历方法,掌握这些技巧可以让你在处理文件系统时事半功倍。

2. 基础方法:os模块的walk和listdir

2.1 os.listdir() - 最简单的文件列表获取

os.listdir()是Python中最基础的文件列表获取方法,它返回指定路径下的所有文件和子目录名称列表:

import os path = '/path/to/your/directory' files = os.listdir(path) print(files)

这个方法有几个特点需要注意:

  • 只返回名称,不包含完整路径
  • 包含文件和目录,不进行区分
  • 不递归子目录
  • 返回顺序取决于文件系统,通常是无序的

提示:如果需要完整路径,可以使用os.path.join()拼接:

full_paths = [os.path.join(path, f) for f in files]

2.2 os.walk() - 递归遍历的金牌工具

os.walk()是Python中递归遍历目录树的"瑞士军刀",它会生成一个三元组(dirpath, dirnames, filenames):

for root, dirs, files in os.walk('/path/to/your/directory'): print(f"当前目录:{root}") print(f"子目录:{dirs}") print(f"文件:{files}")

实际应用中,我经常这样使用:

file_list = [] for root, _, files in os.walk('/path'): for file in files: file_list.append(os.path.join(root, file))

os.walk()有几个实用特性:

  • topdown参数控制遍历顺序(默认True,从上到下)
  • 可以原地修改dirs列表来控制遍历哪些子目录
  • 遵循符号链接(除非设置followlinks=False)

3. 现代方法:pathlib的优雅实现

Python 3.4引入的pathlib模块提供了更面向对象的文件系统操作方式,代码通常更简洁易读。

3.1 Path.glob() - 模式匹配文件查找

from pathlib import Path # 查找所有.py文件(包括子目录) py_files = list(Path('.').rglob('*.py')) # 只查找当前目录的.txt文件 txt_files = list(Path('.').glob('*.txt'))

rglob相当于glob的递归版本,非常方便。pathlib的glob支持这些模式:

  • *匹配任意字符
  • ?匹配单个字符
  • []匹配字符范围
  • **递归匹配(当用在模式中间时)

3.2 Path.iterdir() - 更现代的listdir替代

directory = Path('/path/to/dir') for item in directory.iterdir(): if item.is_file(): print(f"文件:{item.name}") elif item.is_dir(): print(f"目录:{item.name}")

相比os.listdir(),iterdir()的优势在于:

  • 返回Path对象而非字符串
  • 可以直接调用.is_file()/.is_dir()等方法
  • 更符合现代Python风格

4. 高级技巧与性能优化

4.1 处理大目录时的性能考量

当处理包含数十万文件的目录时,性能变得很重要。以下是一些实测有效的优化技巧:

  1. 使用os.scandir()代替os.listdir()

    with os.scandir('/path') as entries: for entry in entries: if entry.is_file(): print(entry.name)

    scandir()在Windows和Linux上性能更好,因为它不需要为每个文件单独调用stat()。

  2. 并行处理文件使用多线程/多进程处理不同子目录:

    from concurrent.futures import ThreadPoolExecutor def process_file(file_path): # 处理单个文件 pass with ThreadPoolExecutor() as executor: for root, _, files in os.walk('/path'): executor.map(process_file, [os.path.join(root, f) for f in files])

4.2 过滤与排除技巧

实际项目中,我们通常需要过滤特定文件:

# 只获取图片文件 image_exts = {'.jpg', '.png', '.gif'} image_files = [ f for f in Path('.').rglob('*') if f.suffix.lower() in image_exts ] # 排除隐藏文件和目录 visible_files = [ f for f in Path('.').rglob('*') if not any(part.startswith('.') for part in f.parts) ]

4.3 处理特殊文件名

有些文件名可能包含特殊字符或编码问题,正确处理很重要:

# 安全地处理可能包含特殊字符的路径 try: with open(filename, 'r', encoding='utf-8') as f: content = f.read() except UnicodeDecodeError: # 尝试其他编码 with open(filename, 'r', encoding='latin-1') as f: content = f.read()

5. 常见问题与解决方案

5.1 权限问题处理

遍历系统目录时经常会遇到权限错误,正确处理方式:

for root, dirs, files in os.walk('/'): try: # 正常处理文件 pass except PermissionError: print(f"无法访问 {root},跳过...") dirs[:] = [] # 跳过这个目录的所有子目录 continue

5.2 符号链接处理

默认情况下,os.walk()会跟随符号链接,可能导致无限循环:

for root, dirs, files in os.walk('/path', followlinks=False): # 处理文件 pass

5.3 内存优化

对于超大目录,避免一次性加载所有文件到内存:

def process_large_dir(path): for root, _, files in os.walk(path): for file in files: filepath = os.path.join(root, file) # 立即处理文件,不保存到列表 process_file(filepath)

5.4 跨平台兼容性

不同操作系统路径分隔符不同,正确处理方式:

# 错误方式(Windows不兼容) path = 'folder/file.txt' # 正确方式 path = os.path.join('folder', 'file.txt') # 或者 path = Path('folder') / 'file.txt'

6. 实际应用案例

6.1 项目源代码统计

统计项目中各种语言的代码行数:

def count_lines(filepath): with open(filepath, 'r', encoding='utf-8', errors='ignore') as f: return sum(1 for _ in f) stats = {} for ext in ['.py', '.js', '.html']: files = Path('.').rglob(f'*{ext}') stats[ext] = sum(count_lines(f) for f in files if f.is_file())

6.2 批量重命名文件

为目录下所有图片添加前缀:

for i, img in enumerate(Path('photos').glob('*.jpg'), 1): new_name = f"vacation_{i:03d}{img.suffix}" img.rename(img.with_name(new_name))

6.3 查找重复文件

通过MD5哈希查找重复文件:

import hashlib def file_hash(filepath): hasher = hashlib.md5() with open(filepath, 'rb') as f: for chunk in iter(lambda: f.read(4096), b''): hasher.update(chunk) return hasher.hexdigest() file_hashes = {} for file in Path('.').rglob('*'): if file.is_file(): h = file_hash(file) file_hashes.setdefault(h, []).append(file)

7. 性能对比与选择建议

不同方法的性能特点:

方法特点适用场景
os.listdir()基础,只返回名称简单列出当前目录内容
os.scandir()性能更好,返回更多信息需要文件类型等元信息
os.walk()递归遍历,功能全面需要处理整个目录树
Path.glob()模式匹配,代码简洁需要按模式查找文件
Path.rglob()递归模式匹配需要递归查找特定模式文件

选择建议:

  • 简单需求:优先考虑pathlib,代码更简洁
  • 性能关键:考虑os.scandir()和并行处理
  • 复杂过滤:结合生成器表达式和条件判断
  • 跨平台:使用pathlib或os.path.join

8. 个人经验分享

在实际项目中,我总结出几个有用的经验:

  1. 路径处理统一使用pathlib:自从Python 3.6后,pathlib性能已经足够好,而且代码更清晰。我几乎不再直接使用os.path模块。

  2. 尽早处理异常:文件系统操作充满不确定性(文件被删除、权限变化等),在遍历开始时就要考虑异常处理。

  3. 使用生成器节省内存:对于大目录,使用生成器表达式而不是列表推导式可以显著减少内存使用:

    # 内存友好方式 py_files = (p for p in Path('.').rglob('*.py') if p.is_file())
  4. 记录遍历进度:长时间运行的遍历任务应该记录进度:

    for i, file in enumerate(Path('.').rglob('*'), 1): if i % 100 == 0: print(f"已处理 {i} 个文件...")
  5. 考虑文件名编码:特别是在跨平台项目中,Linux和Windows对文件名编码处理不同,建议统一使用utf-8并做好异常处理。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询