From f7c820b0f4ba536ec0f2b70c66b071659918bf0a Mon Sep 17 00:00:00 2001 From: bruce Date: Mon, 3 Aug 2026 22:01:22 +0800 Subject: [PATCH] =?UTF-8?q?feat(python=E8=BF=9B=E9=98=B6):=20=E6=96=B0?= =?UTF-8?q?=E5=A2=9E=E6=96=87=E4=BB=B6=E4=B8=8E=E7=9B=AE=E5=BD=95=E6=93=8D?= =?UTF-8?q?=E4=BD=9C=E8=AF=BE=E7=A8=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- 02_python进阶/2_2_文件与目录操作/.gitignore | 4 + 02_python进阶/2_2_文件与目录操作/README.md | 405 ++++++++++++++++++ .../file_directory_example.py | 71 +++ 02_python进阶/2_2_文件与目录操作/practice.py | 45 ++ README.md | 26 +- 5 files changed, 538 insertions(+), 13 deletions(-) create mode 100644 02_python进阶/2_2_文件与目录操作/.gitignore create mode 100644 02_python进阶/2_2_文件与目录操作/README.md create mode 100644 02_python进阶/2_2_文件与目录操作/file_directory_example.py create mode 100644 02_python进阶/2_2_文件与目录操作/practice.py diff --git a/02_python进阶/2_2_文件与目录操作/.gitignore b/02_python进阶/2_2_文件与目录操作/.gitignore new file mode 100644 index 0000000..d02008e --- /dev/null +++ b/02_python进阶/2_2_文件与目录操作/.gitignore @@ -0,0 +1,4 @@ +# 本课示例和练习运行时生成的数据。 +# 这些内容可以重新生成,不需要纳入 Git 版本管理。 +runtime_data/ +practice_data/ diff --git a/02_python进阶/2_2_文件与目录操作/README.md b/02_python进阶/2_2_文件与目录操作/README.md new file mode 100644 index 0000000..12d88a8 --- /dev/null +++ b/02_python进阶/2_2_文件与目录操作/README.md @@ -0,0 +1,405 @@ +# 第 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` 中; +- 运行两次练习,程序都不会崩溃; +- 最终日记内容不会因为重复运行而无限增加。 diff --git a/02_python进阶/2_2_文件与目录操作/file_directory_example.py b/02_python进阶/2_2_文件与目录操作/file_directory_example.py new file mode 100644 index 0000000..2149b4d --- /dev/null +++ b/02_python进阶/2_2_文件与目录操作/file_directory_example.py @@ -0,0 +1,71 @@ +# 第 2-2 课完整示例:文件与目录操作 +# +# 本程序只会在当前课程目录的 runtime_data 文件夹中创建和修改文件。 +# runtime_data 已被本课的 .gitignore 忽略,不会污染 Git 变更记录。 + +from pathlib import Path + + +# __file__ 表示当前 Python 文件的位置。 +# parent 表示当前文件所在的目录。 +LESSON_DIR = Path(__file__).parent +DATA_DIR = LESSON_DIR / "runtime_data" +NOTES_FILE = DATA_DIR / "learning_notes.txt" + + +def create_data_directory(): + """创建保存示例数据的目录。""" + # parents=True 表示需要时一并创建上级目录。 + # exist_ok=True 表示目录已经存在时不报错。 + DATA_DIR.mkdir(parents=True, exist_ok=True) + print(f"数据目录已准备:{DATA_DIR.name}") + + +def write_initial_notes(): + """写入初始学习记录。""" + notes = "第一条:学习使用 Path 表示路径。\n" + + # write_text() 会覆盖文件原有内容。 + # encoding="utf-8" 可以正确保存中文。 + NOTES_FILE.write_text(notes, encoding="utf-8") + print(f"初始内容已写入:{NOTES_FILE.name}") + + +def append_note(): + """在文件末尾追加一条学习记录。""" + # open() 打开文件后,需要在操作结束时关闭文件。 + # with 语句会帮助我们自动关闭文件。 + # 模式 "a" 表示追加,不会清空原有内容。 + with NOTES_FILE.open("a", encoding="utf-8") as file: + file.write("第二条:学习读取和追加文件。\n") + + print("新的学习记录已追加。") + + +def read_notes(): + """读取并输出全部学习记录。""" + content = NOTES_FILE.read_text(encoding="utf-8") + print("学习记录内容:") + print(content, end="") + + +def show_directory_contents(): + """遍历并显示数据目录中的内容。""" + print("数据目录中的文件:") + + for path in DATA_DIR.iterdir(): + if path.is_file(): + print(f"- {path.name}") + + +def main(): + """按照安全顺序运行文件与目录示例。""" + create_data_directory() + write_initial_notes() + append_note() + read_notes() + show_directory_contents() + + +if __name__ == "__main__": + main() diff --git a/02_python进阶/2_2_文件与目录操作/practice.py b/02_python进阶/2_2_文件与目录操作/practice.py new file mode 100644 index 0000000..7cf102d --- /dev/null +++ b/02_python进阶/2_2_文件与目录操作/practice.py @@ -0,0 +1,45 @@ +# 第 2-2 课课堂练习:文件与目录操作 +# +# 请先阅读讲义并运行完整示例,再按照顺序完成练习。 +# 所有练习数据只能保存在当前课程目录的 practice_data 文件夹中。 +# 不要使用本课尚未要求的删除操作,也不要修改项目中的其他文件。 + + +# 第一部分:准备路径 +# 1. 从 pathlib 导入 Path。 +# 2. 使用 Path(__file__).parent 得到当前课程目录。 +# 3. 准备 practice_data 目录路径。 +# 4. 准备 practice_data/diary.txt 文件路径。 + +# 第二部分:创建目录 +# 1. 使用 mkdir(parents=True, exist_ok=True) 创建 practice_data。 +# 2. 输出“练习目录已准备。”。 +# 3. 连续运行两次,确认第二次运行不会报错。 +# 第三部分:写入文件 +# 1. 准备字符串“今天学习了文件与目录。\n”。 +# 2. 使用 write_text() 写入 diary.txt。 +# 3. 明确指定 encoding="utf-8"。 +# 4. 输出“日记已写入。”。 +# 第四部分:追加内容 +# 1. 使用 with 和 open("a", encoding="utf-8") 打开 diary.txt。 +# 2. 追加“我会使用 Path 处理路径。\n”。 +# 3. 输出“日记已追加。”。 +# 第五部分:读取内容 +# 1. 使用 read_text(encoding="utf-8") 读取 diary.txt。 +# 2. 输出标题“日记内容:”。 +# 3. 输出读取到的全部内容。 + +# 第六部分:检查和遍历 +# 1. 使用 exists() 检查 diary.txt 是否存在。 +# 2. 存在时输出“日记文件存在。”。 +# 3. 使用 iterdir() 遍历 practice_data。 +# 4. 只输出文件,并通过 name 属性显示文件名。 + +# 完成后自查: +# 1. 是否始终使用 Path 组合路径,而不是手工拼接斜杠; +# 2. 是否把练习数据限制在 practice_data 中; +# 3. 写入和读取中文时是否指定 utf-8; +# 4. 是否知道 write_text() 会覆盖原内容; +# 5. 是否知道追加模式 "a" 不会清空原内容; +# 6. 是否能解释 with 为什么可以帮助关闭文件; +# 7. 是否能使用 exists()、is_file() 和 iterdir()。 diff --git a/README.md b/README.md index 9b8e0d3..84d3372 100644 --- a/README.md +++ b/README.md @@ -189,12 +189,12 @@ Python/ ## 当前学习进度 - 当前阶段:第二阶段——Python 进阶。 -- 当前课程:`2_1_模块与包`。 -- 当前状态:正在学习模块与包。 -- 已完成课程:`1_1_hello_world` 至 `1_14_python基础综合项目`。 -- 学习中的课程:`2_1_模块与包`。 -- 已创建课程目录:第一阶段全部课程,以及 `02_python进阶/2_1_模块与包/`。 -- 下一步:完成模块与包练习,然后学习文件和目录操作。 +- 当前课程:`2_2_文件与目录操作`。 +- 当前状态:正在学习文件与目录操作。 +- 已完成课程:`1_1_hello_world` 至 `1_14_python基础综合项目`,以及 `2_1_模块与包`。 +- 学习中的课程:`2_2_文件与目录操作`。 +- 已创建课程目录:第一阶段全部课程,以及 `02_python进阶/2_1_模块与包/`、`02_python进阶/2_2_文件与目录操作/`。 +- 下一步:完成文件与目录操作练习,然后学习异常处理。 ## 建议环境 @@ -209,12 +209,12 @@ Python/ ## 下一步 -当前正在学习模块与包。本课将学习: +上一课“模块与包”已经完成。当前正在学习文件与目录操作,本课将学习: -1. 模块是什么,以及为什么要拆分文件; -2. 使用 `import` 导入模块; -3. 使用 `from...import...` 导入指定内容; -4. 理解 `__name__ == "__main__"`; -5. 创建和使用自己的模块与包。 +1. 文件、目录和路径的基本含义; +2. 使用 `Path` 表示和组合路径; +3. 创建练习目录; +4. 写入、追加和读取文本文件; +5. 检查路径并遍历目录内容。 -完成本课练习并确认继续后,下一课将学习文件和目录操作。 +完成本课练习并确认继续后,下一课将学习异常处理。