12 · 文件 I/O 与序列化
导语:程序要「落地」,就离不开读写文件------把结果存成 CSV 给 Excel 打开,把数据存成 JSON 传给前端,把状态存成 pickle 下次接着用。这一篇讲清 Python 的文件读写、现代路径操作 pathlib,以及 CSV、JSON、pickle 三种序列化方式。
1. open 与 with 语句
读文件用 open(),配合 with 语句自动关闭:
python
with open("data.txt", encoding="utf-8") as f:
content = f.read()
print(content)
with 会自动关闭文件 (即使中途出错),不用手动 f.close()。这是读写文件的标准姿势。
几种常用模式:
| 模式 | 含义 | 文件不存在时 |
|---|---|---|
"r" |
只读(默认) | 报错 |
"w" |
写入(覆盖) | 自动创建 |
"a" |
追加 | 自动创建 |
"rb" |
二进制读 | 报错 |
"wb" |
二进制写 | 自动创建 |
python
# 逐行读取(大文件推荐,省内存)
with open("data.txt", encoding="utf-8") as f:
for line in f:
print(line, end="")
# 写入
with open("out.txt", "w", encoding="utf-8") as f:
f.write("第一行\n")
f.write("第二行\n")
🚫 避坑 :读写文本 文件务必加
encoding="utf-8"。Windows 上默认编码可能是 GBK,不加会导致中文乱码或报错。"rb"/"wb"用于图片、音频等二进制 文件,此时不要加encoding。
2. pathlib:现代路径操作
传统 os.path 用字符串拼路径,容易出错。pathlib 用面向对象的方式操作路径,是现代首选:
python
from pathlib import Path
# 当前目录
base = Path(".") / "data" # 用 / 拼接路径,直观
base.mkdir(exist_ok=True) # 建目录,已存在不报错
file = base / "report.json"
file.write_text('{"ok": true}', encoding="utf-8") # 直接写文本
print(file.exists()) # 输出:True
print(file.name) # 输出:report.json
print(file.suffix) # 输出:.json
# 遍历目录下所有 .txt 文件
for txt in Path(".").glob("*.txt"):
print(txt)
Path / "子目录"用/拼路径,跨平台。read_text/write_text一行搞定读写。glob()按模式匹配文件,exists()、name、suffix都是常用属性。
💡 转行速览 :
pathlib约等于 Java 的java.nio.file.Path,把路径当对象而非字符串处理。
3. CSV 读写(csv 模块)
CSV 是「逗号分隔值」,Excel 和数据分析最通用的表格格式。用标准库 csv 模块:
python
import csv
# 写入 CSV
rows = [
["订单号", "金额", "状态"],
["A001", "199.0", "已支付"],
["A002", "299.0", "待支付"],
]
with open("orders.csv", "w", newline="", encoding="utf-8") as f:
writer = csv.writer(f)
writer.writerows(rows)
# 读取 CSV
with open("orders.csv", encoding="utf-8") as f:
reader = csv.DictReader(f) # 用 DictReader,每行变成字典
for row in reader:
print(row["订单号"], row["金额"])
# 输出:
# A001 199.0
# A002 299.0
🚫 避坑 :写 CSV 时
open要加newline=""(尤其是 Windows),否则会多出空行。DictReader用第一行做键,读起来比reader的下标更清晰,推荐。
4. JSON 序列化与反序列化
JSON 是互联网数据交换的标准,和 Python 字典几乎一一对应。四个核心函数:
| 函数 | 方向 | 说明 |
|---|---|---|
json.dumps(obj) |
对象 → 字符串 | 序列化 |
json.loads(text) |
字符串 → 对象 | 反序列化 |
json.dump(obj, f) |
对象 → 文件 | 写文件 |
json.load(f) |
文件 → 对象 | 读文件 |
python
import json
order = {
"order_id": "A20260829001",
"customer": {"name": "赵敏", "city": "杭州"},
"total": 9797.0,
}
# 序列化成字符串(ensure_ascii=False 让中文不转成 \uXXXX)
text = json.dumps(order, ensure_ascii=False, indent=2)
print(text)
# 输出:
# {
# "order_id": "A20260829001",
# "customer": {
# "name": "赵敏",
# "city": "杭州"
# },
# "total": 9797.0
# }
# 反序列化回字典
data = json.loads(text)
print(data["customer"]["name"]) # 输出:赵敏
# 直接写/读文件
with open("order.json", "w", encoding="utf-8") as f:
json.dump(order, f, ensure_ascii=False, indent=2)
with open("order.json", encoding="utf-8") as f:
loaded = json.load(f)
print(loaded["total"]) # 输出:9797.0
💡 提示 :
ensure_ascii=False让中文保持原样、可读;indent=2让输出带缩进、更美观。但注意 :JSON 里没有「元组」「日期」等类型,json.dumps遇到它们会报错,需要先转换(如日期转字符串)。
5. pickle 简述
pickle 能把任意 Python 对象序列化成字节流(JSON 只能存基本类型):
python
import pickle
config = {"theme": "dark", "sizes": (16, 24), "enabled": True}
# 序列化到文件
with open("config.pkl", "wb") as f: # 二进制模式
pickle.dump(config, f)
# 反序列化
with open("config.pkl", "rb") as f:
loaded = pickle.load(f)
print(loaded["sizes"]) # 输出:(16, 24) ------ 元组被完整保留
🚫 避坑 :pickle 只能用于自己可控的、可信的数据 。绝不要
pickle.load来自不可信来源的文件------反序列化可以执行任意代码,是严重的安全风险。跨语言、跨系统交换数据请用 JSON。
6. 实战演练:读取 CSV 汇总成 JSON 报告
需求:读一个订单 CSV 文件,统计总金额、订单数,生成 JSON 报告。
python
import csv
import json
from pathlib import Path
def summarize_orders(csv_path: str, report_path: str) -> None:
"""读取订单 CSV,汇总统计并生成 JSON 报告。"""
orders = []
with open(csv_path, encoding="utf-8") as f:
reader = csv.DictReader(f)
for row in reader:
orders.append(
{
"order_id": row["订单号"],
"amount": float(row["金额"]),
"status": row["状态"],
}
)
paid_orders = [o for o in orders if o["status"] == "已支付"]
report = {
"total_orders": len(orders),
"paid_orders": len(paid_orders),
"total_amount": round(sum(o["amount"] for o in orders), 2),
"paid_amount": round(sum(o["amount"] for o in paid_orders), 2),
}
Path(report_path).write_text(
json.dumps(report, ensure_ascii=False, indent=2),
encoding="utf-8",
)
print(f"报告已生成:{report_path}")
if __name__ == "__main__":
summarize_orders("orders.csv", "report.json")
假设 orders.csv 内容:
text
订单号,金额,状态
A001,199.0,已支付
A002,299.0,待支付
A003,149.0,已支付
生成的 report.json:
json
{
"total_orders": 3,
"paid_orders": 2,
"total_amount": 647.0,
"paid_amount": 348.0
}
这个「CSV 进、JSON 出」的流程,正是日常数据处理最常见的样子。
7. 常见错误与避坑
- 忘写
encoding="utf-8":中文乱码,Windows 上尤其明显。 "w"覆盖了原文件 :只想追加却用了"w",数据被清空。追加要用"a"。- CSV 少了
newline="":Windows 下出现多余空行。 ensure_ascii=True(默认) :中文变\uXXXX,可读性差,加ensure_ascii=False。- 对不可信文件用 pickle:安全风险,改用 JSON。
8. 小结
with open(...)自动关文件;文本读写加encoding="utf-8",二进制用rb/wb。pathlib用/拼路径、read_text/write_text读写、glob匹配,现代首选。csv.DictReader把每行变字典,最易读。json.dumps/loads/dump/load四个函数,ensure_ascii=False保中文,indent美化。- pickle 能存任意对象但只用于可信数据;跨语言交换用 JSON。
9. 练习与思考
- 写一个脚本,读取用户输入的文本,追加写入
diary.txt(用"a"模式)。 - 把一份学生成绩的字典列表
[{"name": "张三", "score": 90}, ...]写成 CSV 文件。 - 用
pathlib列出当前目录下所有.py文件,并打印它们的名字。 - 思考:什么场景该用 JSON、什么场景该用 CSV、什么场景该用 pickle?
💡 提示:第 4 题从「是否结构化」「是否需要跨语言」「是否需要存对象」三个角度思考。
10. 延伸阅读
- Python 官方文档:Reading and Writing Files
- Python 官方文档:pathlib
- 数据库替代文件存储:见本系列第 19 篇
📚 本系列导航
如果觉得有用,欢迎收藏、点赞、转发,让更多人少走弯路。