# 第 2-2 课:文件与目录操作 ## 一、本课目标 完成本课后,你将能够: 1. 解释文件、目录和路径分别是什么; 2. 使用 `Path` 表示和组合路径; 3. 创建练习目录; 4. 安全地写入、追加和读取文本文件; 5. 检查路径是否存在,以及它是不是文件; 6. 遍历目录中的文件; 7. 说明文件操作可能带来的副作用。 ## 二、前置知识 学习本课前,需要掌握: - 字符串、变量和布尔值; - 条件判断与循环; - 函数、参数和返回值; - 模块、导入和程序入口判断。 上一课学习了模块。本课将从 Python 标准库中导入 `pathlib` 模块提供的 `Path`。 ## 三、文件、目录和路径是什么 文件(File)是保存在计算机中的一份数据,例如: - Python 程序 `practice.py`; - 文本文件 `learning_notes.txt`; - 图片文件 `photo.png`。 目录(Directory)也常被称为文件夹,用于组织文件和其他目录。 路径(Path)用于描述文件或目录在计算机中的位置。例如: ```text 02_python进阶/2_2_文件与目录操作/runtime_data/learning_notes.txt ``` 可以把路径理解为从一个位置找到目标文件所经过的路线。 ## 四、为什么使用 `Path` 不同操作系统表示路径的方式可能不同。Windows 常见反斜杠 `\`,其他系统常见正斜杠 `/`。手工拼接路径容易写错。 `pathlib` 是 Python 标准库中的路径处理模块。`Path` 是它提供的路径对象: ```python from pathlib import Path lesson_dir = Path(__file__).parent data_dir = lesson_dir / "runtime_data" ``` 对象(Object)可以暂时理解为“同时保存数据和操作能力的值”。面向对象课程会详细讲解。 这里: - `__file__` 是 Python 自动提供的特殊变量,表示当前文件的位置; - `Path(__file__)` 把位置转换成路径对象; - `.parent` 得到当前文件所在的目录; - `/ "runtime_data"` 在原路径后面组合新的目录名。 这里的 `/` 不是数学除法。`Path` 对象把它设计成了清晰的路径组合写法。 ## 五、相对路径与绝对路径 相对路径(Relative Path)表示相对于当前工作位置的路线: ```text runtime_data/learning_notes.txt ``` 绝对路径(Absolute Path)从磁盘或系统根位置开始,完整描述目标位置。在 Windows 中可能类似: ```text F:\PyCharm\PythonLearn\02_python进阶\2_2_文件与目录操作 ``` 本课通过 `Path(__file__).parent` 取得课程目录。这样无论从项目根目录还是课程目录启动示例,数据都会写入本课自己的 `runtime_data`。 ## 六、创建目录 ```python data_dir.mkdir(parents=True, exist_ok=True) ``` `mkdir()` 表示创建目录: - `parents=True`:需要时一并创建缺少的上级目录; - `exist_ok=True`:目录已经存在时不报错。 如果没有 `exist_ok=True`,第二次运行程序时可能出现: ```text FileExistsError ``` 中文解释:程序想创建的文件或目录已经存在。 ## 七、写入文本文件 ```python notes_file.write_text( "第一条:学习使用 Path 表示路径。\n", encoding="utf-8", ) ``` `write_text()` 会创建文件并写入文本。如果文件已经存在,它会覆盖原有内容。 覆盖意味着旧内容会被替换,可能难以恢复。因此: - 练习时只操作专用数据目录; - 写入前确认路径是否正确; - 不要把重要文件路径交给练习程序; - 中文文本明确使用 `encoding="utf-8"`。 编码(Encoding)是文字与计算机数据之间的转换规则。UTF-8 是常用的文字编码,可以保存中文和多种语言。 字符串中的 `\n` 表示换行。 ## 八、追加文本文件 覆盖写入会清空旧内容。希望在文件末尾增加内容时,可以使用追加模式: ```python with notes_file.open("a", encoding="utf-8") as file: file.write("第二条:学习读取和追加文件。\n") ``` 打开模式(File Mode)`"a"` 中的字母来自 append,表示追加。 `with` 语句会在代码块结束后帮助关闭文件。即使以后文件操作发生错误,也更容易正确释放文件资源。 本课先记住固定结构: ```python with path.open("a", encoding="utf-8") as file: file.write("需要追加的内容") ``` ## 九、读取文本文件 ```python content = notes_file.read_text(encoding="utf-8") print(content) ``` `read_text()` 会读取整个文本文件,并返回一个字符串。 如果文件不存在,可能出现: ```text FileNotFoundError ``` 中文解释:Python 在指定路径没有找到需要读取的文件。 可以先检查文件是否存在: ```python if notes_file.exists(): content = notes_file.read_text(encoding="utf-8") ``` ## 十、检查路径 常用检查方法: ```python path.exists() path.is_file() path.is_dir() ``` - `exists()`:路径是否存在; - `is_file()`:路径是否存在并且是文件; - `is_dir()`:路径是否存在并且是目录。 这些方法返回布尔值 `True` 或 `False`,可以与条件判断配合使用。 ## 十一、遍历目录 ```python for path in data_dir.iterdir(): if path.is_file(): print(path.name) ``` `iterdir()` 会提供目录中的每一项。每一项仍然是 `Path` 对象: - `path.name` 表示文件或目录的名称; - `path.is_file()` 判断它是否是文件。 本课只遍历当前目录,不递归进入更深层目录。递归表示一个操作继续处理内部的子目录,属于后续扩展内容。 ## 十二、示例文件与数据影响 本课文件结构: ```text 02_python进阶/2_2_文件与目录操作/ ├── .gitignore ├── README.md ├── file_directory_example.py └── practice.py ``` 运行示例后会产生: ```text runtime_data/ └── learning_notes.txt ``` 运行练习后应产生: ```text practice_data/ └── diary.txt ``` 这两个数据目录都被本课 `.gitignore` 忽略,不会加入 Git。程序不会操作课程目录之外的文件。 每次运行完整示例时,`learning_notes.txt` 会先被写入初始内容,再追加第二条记录,因此结果不会无限重复增加。 ## 十三、运行方法 在 PowerShell 中进入项目根目录: ```powershell cd F:\PyCharm\PythonLearn ``` 运行完整示例: ```powershell python .\02_python进阶\2_2_文件与目录操作\file_directory_example.py ``` 完成练习后运行: ```powershell python .\02_python进阶\2_2_文件与目录操作\practice.py ``` ## 十四、运行结果 完整示例的预期结果类似: ```text 数据目录已准备:runtime_data 初始内容已写入:learning_notes.txt 新的学习记录已追加。 学习记录内容: 第一条:学习使用 Path 表示路径。 第二条:学习读取和追加文件。 数据目录中的文件: - learning_notes.txt ``` 然后可以打开 `runtime_data/learning_notes.txt`,确认文件内容与终端输出一致。 ## 十五、关键代码执行顺序 运行 `file_directory_example.py` 时: 1. 导入 `Path`; 2. 根据当前文件位置准备课程目录、数据目录和文件路径; 3. 定义文件操作函数; 4. 入口判断成立,调用 `main()`; 5. 创建 `runtime_data`; 6. 覆盖写入初始记录; 7. 在文件末尾追加一条记录; 8. 读取并输出完整内容; 9. 遍历数据目录并输出文件名。 先创建目录再写文件非常重要。目录不存在时,直接写入内部文件会失败。 ## 十六、常见错误 ### 16.1 手工拼接路径 不推荐: ```python file_path = lesson_dir + "/runtime_data/notes.txt" ``` `lesson_dir` 是 `Path` 对象,不能直接与普通字符串这样相加。使用: ```python file_path = lesson_dir / "runtime_data" / "notes.txt" ``` ### 16.2 忘记创建目录 在 `runtime_data` 不存在时直接写入内部文件,会出现 `FileNotFoundError`。应先调用 `mkdir()`。 ### 16.3 忘记指定中文编码 读取或写入中文时未指定 UTF-8,可能出现乱码或编码错误。请在读写两端都使用 `encoding="utf-8"`。 ### 16.4 混淆覆盖和追加 - `write_text()` 会覆盖原内容; - `open("a")` 会在末尾追加内容。 操作重要文件前必须确认自己需要哪一种行为。 ### 16.5 忘记关闭文件 手动调用 `open()` 后如果忘记关闭文件,可能造成资源占用。本课使用 `with` 自动管理文件。 ### 16.6 路径指向项目外部 练习时不要使用系统目录、桌面文件或其他项目文件。把所有练习数据限制在本课的 `practice_data` 中。 ## 十七、课堂练习 打开 `practice.py`,依次完成: 1. 准备练习目录和日记文件路径; 2. 创建 `practice_data`; 3. 写入第一条日记; 4. 追加第二条日记; 5. 读取并输出完整内容; 6. 检查文件是否存在并遍历目录。 每完成一部分就运行一次。不要直接复制参考答案。 ## 十八、参考答案 请先独立完成练习,再展开查看。
查看参考答案 ```python from pathlib import Path lesson_dir = Path(__file__).parent practice_dir = lesson_dir / "practice_data" diary_file = practice_dir / "diary.txt" practice_dir.mkdir(parents=True, exist_ok=True) print("练习目录已准备。") first_entry = "今天学习了文件与目录。\n" diary_file.write_text(first_entry, encoding="utf-8") print("日记已写入。") with diary_file.open("a", encoding="utf-8") as file: file.write("我会使用 Path 处理路径。\n") print("日记已追加。") content = diary_file.read_text(encoding="utf-8") print("日记内容:") print(content, end="") if diary_file.exists(): print("日记文件存在。") print("练习目录中的文件:") for path in practice_dir.iterdir(): if path.is_file(): print(path.name) ```
## 十九、本课小结 - 文件保存数据,目录用于组织文件,路径描述它们的位置; - `Path` 可以清晰地表示和组合路径; - `mkdir()` 可以创建目录; - `write_text()` 可以写入文本,但会覆盖原内容; - `open("a")` 可以追加内容; - `read_text()` 可以读取整个文本文件; - `with` 可以帮助正确关闭文件; - `exists()`、`is_file()` 和 `is_dir()` 可以检查路径; - `iterdir()` 可以遍历目录; - 文件操作存在副作用,必须限制操作范围并确认目标路径。 ## 二十、验收标准 完成本课时,应满足: - 可以解释文件、目录、相对路径和绝对路径; - 可以使用 `Path(__file__).parent` 定位当前课程目录; - 可以使用 `/` 组合 `Path` 路径; - 可以创建已经存在也不会报错的目录; - 可以正确写入、追加和读取中文文本; - 可以解释覆盖写入与追加写入的区别; - 可以使用 `with` 打开文件; - 可以检查路径并遍历目录中的文件; - 练习数据只出现在 `practice_data` 中; - 运行两次练习,程序都不会崩溃; - 最终日记内容不会因为重复运行而无限增加。