Day 12:【99天精通Python】文件操作 - 让数据持久化保存
前言
欢迎来到第12天!
在前面的11天里,我们写的所有程序,数据都保存在内存中。一旦程序运行结束或者电脑关机,那些辛苦计算出来的结果、用户输入的信息瞬间就消失了。
为了让数据能够长久保存(持久化),我们需要把数据写入到磁盘上的文件中。今天,我们将学习Python中的**文件I/O(输入/输出)**操作。学会了这一招,你就能做很多实用的事情了,比如写日志、处理Excel数据(CSV)、读取配置文件等。
本节内容:
- 文件的打开与关闭
- 最佳实践:
with语句 - 读取文件内容(全量读、按行读)
- 写入文件内容(覆盖写、追加写)
- 文件操作模式汇总
- 实战练习
一、文件的基本操作流程
操作文件就像把大象装进冰箱,总共分三步:
- 打开文件(Open)
- 读写内容(Read/Write)
- 关闭文件(Close)
1.1 打开与关闭
使用内置函数open()打开文件,使用.close()方法关闭文件。
# 1. 打开文件 (如果文件不存在,读取模式下会报错)# encoding='utf-8' 非常重要,防止中文乱码f=open("test.txt",mode="w",encoding="utf-8")# 2. 写入内容f.write("Hello, World!\n")f.write("这是我的第一个文件。")# 3. 关闭文件 (必须做!否则可能导致数据丢失或资源占用)f.close()1.2 为什么必须关闭文件?
如果不关闭文件:
- 数据可能还在缓冲区,没真正写入磁盘。
- 操作系统限制同时打开的文件数量,打开太多会报错。
- 可能会导致文件被锁定,其他程序无法访问。
1.3 最佳实践:with 语句 (上下文管理器)
为了防止忘记写f.close(),或者程序在读写过程中出错导致无法关闭文件,Python 提供了with语句。
它会在代码块执行完毕后(无论是否报错),自动关闭文件。
# 推荐写法withopen("test.txt","w",encoding="utf-8")asf:f.write("使用with语句自动关闭文件,真香!")# 离开缩进块后,文件已自动关闭二、读取文件
假设我们有一个data.txt,内容如下:
Python Java C++ Go2.1 读取全部内容 read()
withopen("data.txt","r",encoding="utf-8")asf:content=f.read()print(content)注意:如果文件非常大(比如几GB),不要用
read(),否则内存会爆掉。
2.2 读取一行 readline()
每次只读一行,适合逐行处理。
withopen("data.txt","r",encoding="utf-8")asf:line1=f.readline()line2=f.readline()print(f"第一行:{line1.strip()}")# strip() 去除末尾换行符print(f"第二行:{line2.strip()}")2.3 读取所有行 readlines()
读取所有行并返回一个列表。
withopen("data.txt","r",encoding="utf-8")asf:lines=f.readlines()print(lines)# ['Python\n', 'Java\n', 'C++\n', 'Go']2.4 最常用的遍历方式
直接对文件对象进行for循环,这是最省内存、最高效的方式。
withopen("data.txt","r",encoding="utf-8")asf:forlineinf:print(line.strip())三、写入文件
写入主要有两种模式:覆盖 (w)和追加 (a)。
3.1 覆盖写入 (w - Write)
- 如果文件不存在:创建新文件。
- 如果文件已存在:清空原内容,重新写入。
withopen("output.txt","w",encoding="utf-8")asf:f.write("旧的内容被清空了。\n")f.write("这是新的内容。")3.2 追加写入 (a - Append)
- 如果文件不存在:创建新文件。
- 如果文件已存在:在文件末尾添加内容,原内容保留。
withopen("output.txt","a",encoding="utf-8")asf:f.write("\n这是追加的一行。")四、文件操作模式汇总
open(file, mode=...)中的mode参数决定了操作方式。
| 模式 | 描述 | 文件不存在时 | 文件存在时 |
|---|---|---|---|
r | 只读(默认) | 报错 | 读取 |
w | 只写 | 创建新文件 | 清空并覆盖 |
a | 追加 | 创建新文件 | 末尾追加 |
x | 独占创建 | 创建新文件 | 报错 (防止覆盖) |
b | 二进制模式 (如图片、音频) | - | - |
+ | 更新 (可读可写) | - | - |
常见组合:
rb: 读取二进制文件 (图片)wb: 写入二进制文件r+: 读写模式 (文件指针在开头)a+: 追加读写模式 (文件指针在末尾)
五、处理二进制文件 (图片/视频)
复制一张图片需要使用rb(Read Binary) 和wb(Write Binary) 模式。
# 文件复制工具defcopy_file(src,dst):try:withopen(src,"rb")asf_src:content=f_src.read()withopen(dst,"wb")asf_dst:f_dst.write(content)print("复制成功!")exceptFileNotFoundError:print("源文件不存在!")# copy_file("photo.jpg", "photo_backup.jpg")六、实战练习
练习1:简易日记本
编写一个程序,让用户输入内容,保存到diary.txt中。每次运行程序都能追加新日记,并自动记录时间。
importdatetimedefwrite_diary():now=datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")print("请输入日记内容 (输入 'q' 退出):")content=input("> ")ifcontent.lower()=='q':return# 使用追加模式 'a'withopen("diary.txt","a",encoding="utf-8")asf:f.write(f"[{now}]{content}\n")print("日记已保存!")# 运行write_diary()练习2:给代码加行号
读取一个 Python 文件,给每一行前面加上行号,并保存为新文件。
defadd_line_numbers(input_file,output_file):try:withopen(input_file,"r",encoding="utf-8")asf_in:lines=f_in.readlines()withopen(output_file,"w",encoding="utf-8")asf_out:forindex,lineinenumerate(lines,1):# 格式化:行号占4位,右对齐f_out.write(f"{index:>4}|{line}")print(f"处理完成,已保存至{output_file}")exceptFileNotFoundError:print("文件未找到!")# 假设当前目录下有 test.py# add_line_numbers("test.py", "test_numbered.txt")七、常见问题
Q1:UnicodeDecodeError是什么鬼?
这是最常见的错误,通常是因为读取文件时编码格式不对。
- Windows 默认编码可能是
GBK。 - Linux/Mac 默认是
UTF-8。 - 解决方法:在
open()中始终显式指定encoding='utf-8'。
Q2:路径中的反斜杠\报错?
在 Windows 路径中,\是转义字符(如\n,\t)。
- 错误写法:
open("C:\new\test.txt")(\n被当成换行了) - 正确写法1 (双反斜杠):
"C:\\new\\test.txt" - 正确写法2 (原始字符串 r):
r"C:\new\test.txt" - 正确写法3 (推荐,使用正斜杠):
"C:/new/test.txt"(Windows也支持)
八、小结
关键要点:
- 始终使用
with open(...)来管理文件,安全又省心。 - 始终指定
encoding='utf-8',远离乱码烦恼。 - 处理大文件时,使用
for line in f逐行读取,不要一次性read()。 - 区分
w(清空写) 和a(追加写) 的区别。
九、课后作业
- 文件统计:下载或创建一个英文文本文件(如一篇新闻),统计其中共有多少行、多少个字符(不含空格)。
- 数据清洗:有一个
scores.txt,每一行是一个学生成绩(如Tom:85)。请读取文件,计算平均分,并将不及格(<60)的学生名单写入fail_list.txt。 - 批量修改:编写一个脚本,将当前目录下所有
.txt文件内的 “Hello” 替换为 “Hi”。
下节预告
Day 13:模块与包 (Modules & Packages)- 代码写多了,全堆在一个文件里太乱了。明天我们学习如何像搭积木一样管理和组织代码!
系列导航:
- 上一篇:Day 11 - 函数进阶
- 下一篇:Day 13 - 模块与包(待更新)