跳转至

8.5 文件与目录——目录操作

内容梗概

本节课讲解非常实用的目录操作。老师以"批量转码课程录像并上传 B 站"的日常需求引入,介绍用 os 标准库实现目录遍历、创建目录、获取文件信息、删除与重命名等操作。课上通过一个完整示例——输入源目录和目标目录,把源目录下所有文件(含子目录)按体积从大到小排序、加序号重命名并移动到新目录——逐步讲解了 os.listdir 配合递归的遍历写法、os.path.join 的路径拼接、os.path.exists / os.mkdir / os.stat / os.path.basename / os.path.splitext / os.rename 等 API。随后介绍了更高效的 os.walk 遍历方式,最后用 Python 3.4 引入的 pathlib(Path 对象)重写同一示例,展示其简洁性。

知识点详解

引入与 os 库概览(约 00:00 - 03:12)

  • 实际场景:每次课程回放都要遍历录像文件、批量转码成符合 B 站标准的格式再上传——遍历目录是最常见的需求之一。
  • Python 提供大量内置**标准库**,最基础的之一是 os(Operating System),负责与操作系统相关的操作。
  • 使用前必须 import os。常见功能:目录遍历(两种方式)、创建文件夹、获取文件信息(大小、创建日期等)、删除文件/目录、重命名(即剪切/移动,macOS 下叫 rename)、路径字符串处理。默认参数细节建议直接看文档。

示例功能与效果演示(约 03:12 - 08:00)

  • 示例:输入要遍历的目录路径和一个新目录名,程序把源目录(含子目录)下所有文件按**体积从大到小**排序,加序号前缀重命名后放进新目录。
  • 应用拓展:遍历照片文件夹,对每个文件算**哈希值**,哈希一致即内容重复,可自动找出并删除重复文件——攒了十几年照片人工去重不现实,用 Python 很简单。(提示:作业涉及遍历,但不是排序这个。)

用 listdir + 递归遍历目录(约 08:04 - 19:45)

  • 自定义函数 walk_dir(path) 遍历目录:
  • os.listdir(path):接收目录路径,返回该目录下所有文件和文件夹名组成的**列表**;但它**不会深入子目录**,只列出当前层。
  • 原始字符串:写 Windows 路径时用 r'...' 避免反斜杠转义;注意**原始字符串不能以奇数个反斜杠结尾**(2 个可以,3 个不行),这是 Python 的特点,记住即可。
  • 路径拼接:必须用 os.path.join(dir, filename),不要自己用字符串加 '\\':
    • Windows 路径分隔符是反斜杠,Linux 是斜杠,手写拼接的代码在 Linux 下跑不了,Python 程序本应跨平台可移植;
    • os.path.join 自动判断操作系统加分隔符,还能避免用户输入路径末尾已带反斜杠导致的重复分隔符问题。
  • os.path.isdir(path):判断路径是目录还是文件(返回 True/False)。
  • 递归逻辑:遇到子目录就递归调用 walk_dir 并把结果 += 拼接到 result 列表;遇到文件直接 append。最终 result 里只有文件、没有目录。递归效率不高(之前讲过),但逻辑清晰。
  • 用 input 获取要遍历的目录和输出目录。
  • os.path.exists(path):判断路径(文件或目录)是否存在;不存在时用 os.mkdir 创建目录(存在则跳过不重复创建)。

获取文件大小并排序(约 21:45 - 24:45)

  • os.stat(path):传入路径返回一个对象,内含大小、创建时间、权限等多种信息;取大小属性即可。
  • 把文件列表转成**字典**:key 是文件路径,value 是文件大小。
  • 用之前"数据类型进阶"学过的方法按字典的 value 排序(不记得可回看录像)。
  • 老师的写代码习惯:每写一段就 print 一下验证前面是否成功,基本不需要专门调试。

重命名并移动(约 24:45 - 29:05)

  • 初始化编号 1,每处理一个文件编号加 1(最大的文件是 1 号)。
  • os.path.basename(path):取路径最后一部分(文件名含扩展名,如 1.txt),去掉前面所有目录。
  • os.path.splitext(basename):split + extension,把文件名拆成元组 ('1', '.txt')——前者赋给 name、后者赋给 ext,以便在中间插入序号。
  • 新文件名 = 编号_原文件名 + 扩展名,再用 os.path.join 与新目录拼成完整目标路径,最后 os.rename(原路径, 新路径) 完成移动(rename 即剪切)。

第二种遍历方式:os.walk(约 29:07 - 33:45)

  • os.walk(path):返回遍历到的每一级目录本身、其子目录列表(dirs)、其文件列表(files),并自动向下层下探,无需手写递归,效率更高。
  • 演示打印每一轮的 root, dirs, files:第一轮 root 是 backup(2 个子目录、无文件),随后分别以子目录为 root 继续下探,直到没有可遍历内容结束。
  • 用法:每轮遍历 files 列表,把文件名与当前 root 用 os.path.join 拼接后 append 进结果列表即可。

pathlib:面向对象的路径操作(约 33:45 - 39:43)

  • os.path 从 Python 2 时代就有,但它是在**路径字符串**基础上做拼接等操作,没有"路径"这个对象,用起来繁琐。
  • Python 3.4 引入 pathlib,提供 Path 对象(类):把路径字符串通过构造函数(类比 C++ 构造函数)转成 Path 对象,之后对路径的所有操作都直接操作这个对象。
  • 对应功能:两种遍历、创建文件夹、统计文件信息、删除文件/目录、重命名、拼接路径。拼接路径直接用**斜杠运算符 /**,不用 os.path.join。
  • 用 pathlib 重写同一示例:
  • Path(目录).glob(...) 遍历目录下所有文件(返回列表,既含文件也含文件夹,需过滤掉文件夹只留文件);
  • Path(新目录).mkdir(exist_ok=True):把"目录是否已存在"的判断隐含到参数里,存在也不报错;
  • 文件名部分直接用属性:.stem(不含路径、不含后缀的纯文件名)、.suffix(后缀),不再用 basename + splitext;
  • 用 / 运算符拼接输出目录与新文件名,再移动——代码大幅简化。
  • 为什么不一上来就教 pathlib:现在仍是新老交替的年代,很多开源项目还在用 os.path,不光要会写代码,还得能看懂别人的代码——这是进阶的基本要求。

示例与演示

  1. 完整示例(os 版):walk_dir 函数用 os.listdir + 递归遍历 backup 目录的所有文件 → os.path.exists / os.mkdir 准备输出目录 → os.stat 取文件大小构建字典 → 按 value 从大到小排序 → 循环中用 basename、splitext 组装 1_xxx.txt 式新文件名 → os.rename 移动。运行后 new 目录里文件按大小排序并重新编号。
  2. 踩坑演示:原始字符串路径末尾多写一个反斜杠导致报错,引出"原始字符串不能以奇数个反斜杠结尾"的规则;缩进多一个空格把自己坑了(Python 缩进严格)。
  3. os.walk 演示:逐轮打印 root / dirs / files,展示自动下探子目录的过程,并改写成收集所有文件路径的代码。
  4. pathlib 版示例:同一功能用 Path.glob、.stem、.suffix、/ 拼接、mkdir(exist_ok=True) 重写,明显更简洁。

重点与难点

  • os.listdir 只列当前层、不进子目录;要遍历整棵树需配合**递归**,或直接用 os.walk(效率更高、自动下探)。
  • 路径拼接永远用 os.path.join(或 pathlib 的 /),不要手写 '\\':跨平台分隔符不同、还可能重复分隔符。
  • Windows 路径用原始字符串 r'...' 防转义;原始字符串不能以奇数个反斜杠结尾。
  • os.rename 同时承担重命名与移动(剪切)功能;os.stat 返回的对象含大小、时间、权限等多种信息。
  • pathlib(3.4+)是当前**最推荐**的写法,但 os.path 在老项目中仍大量存在,两种都要看得懂。
  • 递归遍历返回结果里只有文件没有目录——目录被"展开"处理掉了,这个设计要想清楚。

关联内容

  • 用到大量前置知识:函数与递归(讲函数时学过)、字典及其按 value 排序(数据类型进阶)、序列与列表操作、字符串的原始字符串记法(讲字符串时)。
  • 标准库 os 的使用为第 9 章系统介绍 Python 标准库/第三方库预热;老师明确说"这涉及到下节课的一部分内容"。
  • 遍历目录 + 读文件头是 8.6 节作业"猫鼠游戏"的直接技术基础;去重文件的哈希思路预告了后续"更多酷的内容"。
  • 遍历 + 排序 + 重命名的完整流程是 Python 作为"脚本语言"处理日常事务(整理照片、批量转码)的典型场景。