知识点思维导图
28 个知识节点
参考资料
Python(19) - 文件与 IO 操作
读完后,你应能完成以下任务:
- 绘制“Python(19) - 文件与 IO 操作 / 先给锚点:读文件的心智模型”的关键对象与数据流,解释“关键边界:Python 默认没有"异步读文件"这个日常选项。”,并用源码位置、日志或 Trace 标注证据。
- 为“Python(19) - 文件与 IO 操作 / open 的两个核心参数:mode 和 encoding”设计正常与异常输入,验证“因为 Python 的 open 不指定 encoding 时,会用操作系统默认编码:Linux/Mac 通常是 utf-8(碰巧没事),但 Windows 默认是 GBK,读写中文就会乱码或直接抛 UnicodeDecodeError。”,输出首个偏差位置与回归测试结果。
- 实现“Python(19) - 文件与 IO 操作 / mode:读 / 写 / 追加 / 二进制”的最小代码或配置,检验“⚠️ 头号坑:"w" 模式一打开就立即清空整个文件,哪怕你后面一个字都没写。”,输出命令、结果与 Diff,并说明不适用边界。
你在 Node 里读写文件靠
fs.readFileSync/fs.promises.readFile,解析 JSON 靠JSON.parse,处理 CSV 还得装个papaparse。到了数据处理阶段,读文件这件事会变得很频繁——读训练数据、读配置、把分析结果写回磁盘。本篇解决三个最常见的落地问题:怎么用 Python 安全地读写文本文件、怎么读写 JSON、怎么读写 CSV。好消息是 Python 把 JSON 和 CSV 都做进了标准库,连库都不用装。
一、先给锚点:读文件的心智模型
Node 读文件你大概率写过这两种:
// JS / Node:同步读
const fs = require("fs")
const content = fs.readFileSync("data.txt", "utf-8") // 一次性读成字符串
// JS / Node:Promise 异步读
const content = await fs.promises.readFile("data.txt", "utf-8")
Python 的标准写法只有一种姿势——with open(...),你在第 9 篇(异常处理与上下文管理)已经见过:
先用对照表建立直觉:
| JS / Node | Python | 说明 |
|---|---|---|
fs.readFileSync(path, "utf-8") |
open(path).read() |
一次性读成字符串 |
fs.writeFileSync(path, data) |
open(path, "w").write(data) |
覆盖写 |
fs.appendFileSync(path, data) |
open(path, "a").write(data) |
追加写 |
JSON.parse(str) |
json.loads(str) |
字符串 → 对象 |
JSON.stringify(obj) |
json.dumps(obj) |
对象 → 字符串 |
papaparse(第三方) |
import csv(标准库) |
CSV 读写 |
path.join(a, b) |
Path(a) / b |
拼路径 |
关键边界:Python 默认没有"异步读文件"这个日常选项。Node 里大家习惯
await readFile,但 Python 标准库的open就是同步的,而且这在数据处理脚本里完全够用、也是惯例。异步文件 IO(aiofiles等)是 Web 框架里的特殊需求,不是本篇重点。
二、open 的两个核心参数:mode 和 encoding
open 最常用就两个参数:第二个 mode(怎么打开)和 encoding(按什么编码解码文本)。
2.1 mode:读 / 写 / 追加 / 二进制
对照 Node 的 flag,其实是一回事,只是 Python 的字母更短:
| Python mode | Node flag | 含义 |
|---|---|---|
"r" |
"r" |
只读 |
"w" |
"w" |
覆盖写(清空) |
"a" |
"a" |
追加写 |
"rb" / "wb" |
"r" 配 buffer |
二进制读 / 写 |
⚠️ 头号坑:
"w"模式一打开就立即清空整个文件,哪怕你后面一个字都没写。想保留原内容续写,用"a"。这和 Node 的"w"flag 行为一致,但前端同学常忘。
2.2 encoding:中文乱码的罪魁祸首
为什么必须显式写?因为 Python 的 open 不指定 encoding 时,会用操作系统默认编码:Linux/Mac 通常是 utf-8(碰巧没事),但 Windows 默认是 GBK,读写中文就会乱码或直接抛 UnicodeDecodeError。Node 里你习惯了第二个参数传 "utf-8",Python 这边对应的就是 encoding="utf-8",养成肌肉记忆。
三、四种读取方式:什么时候读全部,什么时候逐行
Node 里你基本就是 readFile 一次性读完。Python 给了更细的粒度,核心区别在"小文件全读" vs "大文件逐行流式读":
最推荐、也是最 Python 的写法——直接把文件对象当成可迭代对象,一行一行流式遍历:
这正对应 Node 里"小文件 readFile、大文件用 stream"的取舍:
// Node 大文件要手动上 stream,写法明显更重
const rl = readline.createInterface({ input: fs.createReadStream("huge.log") })
for await (const line of rl) {
console.log(line)
}
Python 这边一个 for line in f 就达到了同样的"流式、省内存"效果,是它比 Node 顺手的地方。
记忆:
read()全读成一个字符串,readlines()全读成一个列表,for line in f逐行流式(大文件用这个)。
四、写文件:write 与 writelines
⚠️ 坑:
write/writelines都不会帮你加换行符,需要换行得自己在字符串里写\n。这和 Node 的fs.writeFile一样,但容易和
五、JSON:标准库自带,但要分清 4 个函数
前端处理 JSON 就两个函数:JSON.parse 和 JSON.stringify。Python 的 json 模块(标准库,不用装)也是这个思路,但多了一组带文件的版本,新手最容易混。
记住一个规律:带 s 的处理"字符串"(string),不带 s 的直接处理"文件对象"。
| 我要做的事 | JS | Python | 记忆 |
|---|---|---|---|
| JSON 字符串 → 对象 | JSON.parse(str) |
json.loads(str) |
loads = load string |
| 对象 → JSON 字符串 | JSON.stringify(obj) |
json.dumps(obj) |
dumps = dump string |
| 直接从文件读成对象 | 先 readFile 再 parse | json.load(f) |
load 接文件对象 |
| 对象直接写进文件 | 先 stringify 再 writeFile | json.dump(obj, f) |
dump 接文件对象 |
5.1 字符串版:loads / dumps
dumps 有两个前端一定要记的参数:
ensure_ascii=False:不写它(默认True),中文会被转成\uXXXX形式的 Unicode 转义码——比如"你好"会被序列化成"\u4f60\u597d"。要正常显示中文必须加这句。这是 Python 特有的坑,JS 的JSON.stringify不会干这事。indent=2:美化缩进,等价于 JS 的JSON.stringify(obj, null, 2)。
5.2 文件版:load / dump(不带 s,直接接文件)
对照 Node,你会发现 Python 的文件版少写一步:
// Node:读 JSON 得两步——先 readFile 拿字符串,再 parse
const loaded = JSON.parse(fs.readFileSync("config.json", "utf-8"))
// 写也是两步——先 stringify,再 writeFile
fs.writeFileSync("config.json", JSON.stringify(config, null, 2))
⚠️ 类型映射边界:JSON 的
true/false/null解析成 Python 的True/False/None(首字母大写、null变None);反过来 Python 的None会序列化成 JSON 的null。另外 Python 的dict键如果是数字,dumps后会变成字符串键——和 JS 对象一样,JSON 的键永远是字符串。
六、CSV:标准库 csv 模块
CSV(逗号分隔的表格文本)在数据处理里到处都是。前端通常装 papaparse,Python 直接 import csv 即可。
6.1 读 CSV
最常用 csv.DictReader——把每一行读成字典,用表头当 key,比按下标取值清晰得多:
如果不想用表头、就要原始的每行列表,用 csv.reader:
6.2 写 CSV
⚠️ CSV 三大坑(前端高频):
- 读出来的值全是字符串——
row["age"]是"30"不是30,要算数得先int()/float()。newline=""不能省——Python 官方文档明确要求写 CSV 时加它,否则 Windows 下每行之间会多出空行。- CSV 是纯文本、没有类型和嵌套结构。真要做表格分析(筛选、分组、聚合),别用
csv硬撸,下一阶段的 Pandas 才是正解(pd.read_csv一行搞定,详见第 21 篇)。csv模块适合简单的逐行读写。
七、路径处理:用 pathlib 代替字符串拼接
前端拼路径用 path.join("a", "b"),避免手写 / 在不同系统出错。Python 现代写法是 pathlib.Path,它把路径做成对象,用 / 运算符拼接,很优雅:
对照 Node:
| Node | Python (pathlib) |
|---|---|
path.join(a, b) |
Path(a) / b |
path.extname(p) |
p.suffix |
path.basename(p) |
p.name |
fs.existsSync(p) |
p.exists() |
__dirname |
Path(__file__).parent |
⚠️ 相对路径坑(前后端通病):
open("data.txt")里的相对路径,是相对于运行脚本时所在的工作目录(cwd),不是脚本文件本身的位置。在不同目录下python xxx.py启动,可能就找不到文件。稳妥做法:用Path(__file__).parent / "data.txt"锚定到脚本自身位置。
八、最容易踩的坑(前端视角汇总)
"w"模式一开就清空文件——想续写用"a"。- 不写
encoding="utf-8"——Windows 下默认 GBK,中文乱码或报UnicodeDecodeError。读写文本永远显式带上。 json.dumps中文变\uXXXX——加ensure_ascii=False才正常显示中文。load/loads分不清——带s的吃字符串,不带s的吃文件对象。- CSV 值全是字符串——数字要手动
int()/float();写 CSV 别忘newline=""。 - 相对路径相对的是 cwd 不是脚本——用
Path(__file__).parent锚定。 - 大文件别
f.read()一次性读——用for line in f流式逐行,不然爆内存。
九、总结
- 先给锚点:读文件的心智模型:关键边界:Python 默认没有"异步读文件"这个日常选项。
- open 的两个核心参数:mode 和 encoding:对照 Node 的 flag,其实是一回事,只是 Python 的字母更短:
- 四种读取方式:什么时候读全部,什么时候逐行:Node 里你基本就是 readFile 一次性读完。
- 写文件:write 与 writelines:⚠️ 坑:write / writelines 都不会帮你加换行符,需要换行得自己在字符串里写 \n。
- JSON:标准库自带,但要分清 4 个函数:Python 的 json 模块(标准库,不用装)也是这个思路,但多了一组带文件的版本,新手最容易混。
- CSV:标准库 csv 模块:CSV(逗号分隔的表格文本)在数据处理里到处都是。
学完自测
选择所有正确答案;提交后逐项核对判断依据。