第5章 5.3 文件读写与 with 上下文
🎯 开场 3 分钟:为什么要学这个?
上一章我们学会了用正则表达式从一堆乱码里「挖」出手机号、邮箱这些有价值的信息。现在问题来了——挖出来的数据存哪儿?
你肯定遇到过这种情况:写了一个爬虫程序,关掉电脑再打开,数据没了;或者手动复制粘贴结果到 txt 里,结果手抖复制错了行。这种「数据用完就消失」的感觉,就像做饭时没有保鲜盒,食材再好也存不住。
本章我们要解决的就是这个问题:怎么把数据永久保存到文件里,下次想用时还能读出来。学完这章,你就能做出一个真正的「数据存取小工具」,爬虫爬到的数据、清洗完的结果,统统能存进文件里,下次直接拿来用。
🧱 基础 25 分钟:核心概念
什么是文件?用快递盒理解
想象你有一个快递盒:
- 打开快递盒 = 读文件(open + read)
- 往盒子里放东西 = 写文件(open + write)
- 封箱 = 关闭文件(close)
普通快递盒有个问题:如果快递员(程序)突然被车撞了(崩溃),盒子里刚放了一半的东西就全乱了。文件也是这个道理——如果你写了一半程序突然崩掉,文件可能就损坏了。
with 语句就像给快递盒装了个自动封箱器:不管里面发生了什么(正常完成 or 突然报错),离开 with 块的时候文件都会被自动关好。数据不会丢,文件不会坏。

第一个文件操作:读文件
# 打开一个文件,读取内容
with open('notes.txt', 'r', encoding='utf-8') as f:
content = f.read()
print(content)
这 4 行代码在干嘛:
open('notes.txt', 'r', encoding='utf-8')—— 找到名为 notes.txt 的快递盒,用「读」模式打开(r= read)as f—— 给这个快递盒起个代号 f,方便后面使唤f.read()—— 读取盒子里所有内容- 出了
with块 —— 盒子自动封好,不用你操心
注意!
encoding='utf-8'就像快递盒上的「易碎品」标签,告诉系统用国际通用的编码方式来读中文。没有这个,中文可能会变成乱码。
写文件:创建你的第一个笔记
# 打开一个文件,写入内容
with open('diary.txt', 'w', encoding='utf-8') as f:
f.write('今天学会了文件读写!\n')
f.write('明天继续加油!\n')
解释:
- 'w' 模式 = write,会清空文件重新写(如果文件不存在,会自动创建)
- \n 是换行符,让两句话分两行显示
- 运行完后,当前文件夹会多出一个 diary.txt 文件
追加模式:不覆盖的写入
# 追加模式写入,不会删除原有内容
with open('diary.txt', 'a', encoding='utf-8') as f:
f.write('后天学正则表达式!\n')
'a'模式 = append,在文件末尾追加新内容- 之前写的两句话还在,又多了一行

pathlib:让路径操作更优雅
传统文件路径长这样:
# Windows 系统常见写法
file_path = 'C:\\Users\\apple\\Desktop\\notes.txt'
# 或者用反斜杠转义
file_path = 'C:/Users/apple/Desktop/notes.txt'
反斜杠容易引发「转义字符」问题(比如 \n 会被当成换行)。用 pathlib 就像用导航软件,路径问题交给它处理:
from pathlib import Path
# 创建一个「路径对象」,自动适配你的系统
desktop = Path.home() / 'Desktop'
notes_file = desktop / 'notes.txt'
# 读写操作一样用 with
with open(notes_file, 'r', encoding='utf-8') as f:
content = f.read()
说白了:
Path.home() / 'Desktop'相当于「导航到桌面」,/ 'notes.txt'相当于「找到 notes.txt」。不管你用的是 Mac 还是 Windows,Python 都会自动帮你拼对路径。
逐行读取:大文件的正确姿势
如果文件有几万行,一次性 read() 可能会把内存撑爆。正确做法是逐行读取:
with open('big_data.csv', 'r', encoding='utf-8') as f:
for line in f: # 像吃面条一样,一口一口来
print(line.strip()) # strip() 去掉每行末尾的换行符
🔥 实战 35 分钟:3 个递进的小项目
项目 1(5 分钟):自动保存学习笔记
场景:你每天学 Python,想把笔记自动存到文件里。
from datetime import datetime
# 获取今天的日期作为文件名
today = datetime.now().strftime('%Y-%m-%d')
filename = f'笔记_{today}.txt'
# 写入今天的笔记
note_content = """学习进度:
- 学会了正则表达式匹配手机号
- 理解了捕获组的概念
明日计划:
- 继续学习文件读写
"""
with open(filename, 'w', encoding='utf-8') as f:
f.write(note_content)
print(f'笔记已保存到 {filename}')
预期输出:
笔记已保存到 笔记_2026-06-26.txt
解释:这就像给每天的笔记装了一个自动存档功能,文件名自动带上日期,找起来方便。
项目 2(15 分钟):读取 CSV 文件并统计
场景:你有一个 CSV 文件,记录了每天的学习时间,现在要统计这周总共学了多久。
假设 study_log.csv 内容如下:
日期,学习时长(分钟)
2026-06-20,45
2026-06-21,60
2026-06-22,30
2026-06-23,90
2026-06-24,25
2026-06-25,50
代码:
from pathlib import Path
# 读取 CSV 文件
csv_path = Path('study_log.csv')
total_minutes = 0
days_count = 0
with open(csv_path, 'r', encoding='utf-8') as f:
# 跳过第一行(表头)
next(f)
# 逐行读取数据
for line in f:
line = line.strip() # 去掉换行符
if not line: # 跳过空行
continue
# 用 split 按逗号分割
date, minutes = line.split(',')
total_minutes += int(minutes)
days_count += 1
# 计算小时和分钟
hours = total_minutes // 60
minutes = total_minutes % 60
print(f'本周学习 {days_count} 天')
print(f'总计 {hours} 小时 {minutes} 分钟')
预期输出:
本周学习 6 天
总计 5 小时 0 分钟
解释:这就像一个学习打卡计算器,把零散的数据读进来,汇总成一个有意义的统计结果。
项目 3(15 分钟):做个待办清单小工具
场景:做一个命令行待办清单,能添加任务、查看任务、标记完成。所有数据存到文件里,关闭再打开任务还在。
完整代码:
from pathlib import Path
TODO_FILE = Path('todo_list.txt')
def load_tasks():
"""加载任务列表"""
if not TODO_FILE.exists():
return []
with open(TODO_FILE, 'r', encoding='utf-8') as f:
return [line.strip() for line in f if line.strip()]
def save_tasks(tasks):
"""保存任务列表"""
with open(TODO_FILE, 'w', encoding='utf-8') as f:
for task in tasks:
f.write(task + '\n')
def show_tasks(tasks):
"""显示所有任务"""
if not tasks:
print('📝 清单是空的,添加一个任务吧!')
return
print('📋 当前待办清单:')
for i, task in enumerate(tasks, 1):
print(f' {i}. {task}')
def add_task(tasks, new_task):
"""添加新任务"""
if not new_task.strip():
print('❌ 任务内容不能为空!')
return False
tasks.append(f'[ ] {new_task}')
save_tasks(tasks)
print(f'✅ 已添加:{new_task}')
return True
def complete_task(tasks, task_number):
"""标记任务完成"""
if task_number < 1 or task_number > len(tasks):
print('❌ 无效的任务编号!')
return False
task = tasks[task_number - 1]
if task.startswith('[✓]'):
print('⚠️ 这个任务已经完成了!')
return False
tasks[task_number - 1] = task.replace('[ ]', '[✓]')
save_tasks(tasks)
print(f'🎉 已完成:{task[4:]}')
return True
def main():
tasks = load_tasks()
print('=' * 30)
print(' 📝 待办清单小工具')
print('=' * 30)
while True:
print('\n请选择操作:')
print(' 1. 查看清单')
print(' 2. 添加任务')
print(' 3. 标记完成')
print(' 4. 退出')
choice = input('\n你的选择(1/2/3/4):').strip()
if choice == '1':
show_tasks(tasks)
elif choice == '2':
new_task = input('输入新任务:')
add_task(tasks, new_task)
elif choice == '3':
show_tasks(tasks)
if tasks:
num = input('输入要完成的任务编号:')
if num.isdigit():
complete_task(tasks, int(num))
elif choice == '4':
print('👋 下次见!')
break
else:
print('❌ 无效选择,请重新输入')
if __name__ == '__main__':
main()
预期输出(交互示例):
==============================
📝 待办清单小工具
==============================
请选择操作:
1. 查看清单
2. 添加任务
3. 标记完成
4. 退出
你的选择(1/2/3/4):2
输入新任务:学习文件读写
✅ 已添加:学习文件读写
你的选择(1/2/3/4):1
📋 当前待办清单:
1. [ ] 学习文件读写
解释:这个小工具把「数据持久化」的概念用得很彻底——所有任务都存在文件里,关掉程序再打开,数据还在。下次学新东西时,直接运行就能继续用。
💪 进阶 20 分钟:常见坑 + 性能小贴士
坑 1:忘记指定编码,中文变成乱码
# ❌ 错误写法(Windows 系统默认 GBK 编码,读 UTF-8 会乱码)
with open('notes.txt', 'r') as f:
content = f.read()
# ✅ 正确写法
with open('notes.txt', 'r', encoding='utf-8') as f:
content = f.read()
坑 2:文件不存在还硬要读
# ❌ 错误写法(文件不存在会报错 FileNotFoundError)
with open('maybe_exists.txt', 'r') as f:
content = f.read()
# ✅ 正确写法(先检查文件是否存在)
from pathlib import Path
file_path = Path('maybe_exists.txt')
if file_path.exists():
with open(file_path, 'r', encoding='utf-8') as f:
content = f.read()
else:
print('文件不存在')
坑 3:写了模式用成了读模式
# ❌ 错误写法(用 'w' 模式打开后还想读取)
with open('notes.txt', 'w', encoding='utf-8') as f:
f.write('hello')
content = f.read() # 报错:not readable
# ✅ 正确写法(需要读写就用 'r+' 或分开两次 open)
with open('notes.txt', 'r+', encoding='utf-8') as f:
content = f.read()
f.write('\n追加的内容')
坑 4:换行符在不同系统不兼容
# ❌ Windows 换行是 \r\n,Linux/Mac 是 \n
# 手写换行可能在不同系统出问题
content = '第一行\r\n第二行' # 如果在 Mac 上可能显示异常
# ✅ 正确写法:用 \n,Python 会自动处理
content = '第一行\n第二行'
坑 5:with 块内抛异常,文件没关闭
# ❌ 没用 with 的危险写法
f = open('notes.txt', 'w', encoding='utf-8')
f.write('hello')
raise ValueError('出错了!') # 程序在这里崩了
f.close() # 这行永远不会执行,文件没关好
# ✅ 用 with,异常也会自动关闭
with open('notes.txt', 'w', encoding='utf-8') as f:
f.write('hello')
raise ValueError('出错了!') # 没关系,with 会自动关文件
调试技巧:print 大法好
# 读取前先看看文件存不存在
from pathlib import Path
csv_path = Path('study_log.csv')
print(f'文件存在吗?{csv_path.exists()}')
print(f'绝对路径:{csv_path.absolute()}')
# 读到了?打印前几行看看格式对不对
with open(csv_path, 'r', encoding='utf-8') as f:
for i, line in enumerate(f):
if i >= 5: # 只看前 5 行
break
print(f'第{i}行:{repr(line)}') # repr() 显示原始字符
✏️ 练习题
练习 1(2 分钟):抄改文件名
- 输入:把项目 1 的文件名从
笔记_2026-06-26.txt改成我的笔记.txt - 预期输出:运行后生成
我的笔记.txt - 提示:
filename变量改成字符串字面量就行
练习 2(2 分钟):加个完成判断
- 输入:在项目 2 的代码里,加一个判断,如果当天学习时间超过 60 分钟就打印「今天很努力!」
- 预期输出:6 月 23 日(90 分钟)会额外打印「今天很努力!」
- 提示:在
total_minutes += int(minutes)后面加个if int(minutes) > 60: print(...)
练习 3(3 分钟):读取新 CSV
- 输入:新建一个
expense.csv,内容是「日期,消费金额」,统计总消费 - 预期输出:打印「本周总消费:XXX 元」
- 提示:把项目 2 的代码复制过来,改两个变量名就行
练习 4(5 分钟):串个项目 2 和项目 3
- 输入:让项目 3 支持把任务导出到 CSV 文件
- 预期输出:选择「导出」时生成
tasks_export.csv - 提示:参考项目 2 的 CSV 写入方式
练习 5(3 分钟):看图找 bug
- 输入:以下代码运行后报
UnicodeDecodeError,找出原因
with open('chinese.txt', 'r') as f:
print(f.read())
- 预期输出:修复后能正常打印文件内容
- 提示:检查 encoding 参数
作业:做个「每日一句」名言收藏工具
需求描述:做一个命令行小工具,收集你喜欢的名言佳句,数据永久保存在文件里。
功能点:
1. 添加名言(格式:内容 + 作者)
2. 随机展示一条收藏的名言
3. 查看所有收藏
加分项:
1. 支持删除某条名言
2. 启动时随机展示一条
验收标准:
- 能跑起来
- 添加后关闭再打开,数据还在
- 代码有中文注释
提交方式:评论区贴代码或 GitHub 链接
📚 总结 + 资源
本文学了 3 个核心点:
1. open() + with = 安全读写文件不怕崩
2. encoding='utf-8' = 解决中文乱码问题
3. pathlib.Path = 让路径操作更优雅
延伸学习资源:
- Python 官方文档:文件操作 —— 权威、全面、免费
- 《Python编程:从入门到实践》第 8 章 —— 项目驱动,干货满满
- 视频:B 站「小甲鱼 Python 教程」文件操作章节 —— 讲解细致,适合小白
互动钩子:你在工作/学习中有什么数据需要定期保存的?是笔记、账目、还是其他什么?评论区聊聊实现思路,老粉优先回复!
下一章我们要解决一个新问题:文件里的数据格式千变万化,有 CSV、有 JSON、还有 YAML……学会了这些格式,读写数据才能真正做到「收放自如」。

评论(0)