一、永远显式指定编码
with open('data.txt', 'r', encoding='utf-8') as f:
text = f.read()
不写 encoding 时,Python 用 locale.getpreferredencoding()——Linux 上常是 UTF-8,Windows 上常是 GBK。同一份代码在不同机器上行为不同,这是跨平台 bug 的常见来源。
with 保证文件在异常时也能正确关闭,不要用 f = open(...) 然后忘了 close()。
二、模式
| 模式 | 含义 |
|---|---|
r | 读文本(默认),文件不存在报错 |
w | 写文本,清空原内容 |
a | 追加 |
x | 独占创建,文件已存在报错 |
rb / wb | 二进制读写 |
加 + 可读写(r+、w+),但容易搞混指针位置,多数场景用不到。
w 会清空文件,这是最容易造成数据丢失的模式。写之前确认路径,或者先判断 os.path.exists。
三、读的三种方式
f.read() # 一次读全部(大文件会爆内存)
f.readline() # 读一行
f.readlines() # 读所有行成列表(同样占内存)
处理大文件要逐行迭代,文件对象本身是可迭代的,按行流式读取:
with open('big.log', encoding='utf-8') as f:
for line in f:
process(line.rstrip('\n'))
这样内存占用与文件大小无关。几十 GB 的日志也能处理。
如果要按固定大小分块读二进制,用 f.read(8192) 循环。
四、写文件
with open('out.txt', 'w', encoding='utf-8') as f:
f.write('第一行\n')
f.writelines(['a\n', 'b\n']) # 注意:不会自动加换行
writelines 不添加分隔符,每个元素必须自带 \n。
写 JSON 更常用 json.dump:
import json
with open('out.json', 'w', encoding='utf-8') as f:
json.dump(data, f, ensure_ascii=False, indent=2)
ensure_ascii=False 让中文正常输出而不是 \uXXXX;不加这个参数,文件里全是转义序列。
读 JSON:
with open('in.json', encoding='utf-8') as f:
data = json.load(f)
文件带 BOM 时会报 Unexpected UTF-8 BOM,用 encoding='utf-8-sig' 解决(详见 BOM 是什么)。
五、pathlib 比字符串拼路径更好
from pathlib import Path
p = Path('/data/logs') / 'app.log' # 自动处理分隔符
p.exists()
p.suffix # '.log'
p.stem # 'app'
p.parent
p.resolve() # 绝对路径
p.stat().st_size # 文件大小
for f in Path('.').glob('*.py'):
print(f)
Windows 上用 os.path.join 也没问题,但 pathlib 的 / 运算符更直观,跨平台不用关心反斜杠。
六、CSV
import csv
with open('in.csv', encoding='utf-8-sig', newline='') as f:
for row in csv.DictReader(f):
print(row['name'])
newline='' 必须加,否则 Windows 上会出现空行。写 CSV 同理:
with open('out.csv', 'w', encoding='utf-8-sig', newline='') as f:
w = csv.DictWriter(f, fieldnames=['id', 'name'])
w.writeheader()
w.writerows(rows)
utf-8-sig 让 Excel 能正确识别中文。
七、常见问题
| 现象 | 原因 |
|---|---|
UnicodeDecodeError: 'gbk' codec can't decode | 文件是 UTF-8,系统默认按 GBK 读 |
UnicodeDecodeError: invalid start byte | 读到了二进制文件,用 rb |
| 写入后中文变乱码 | 读时 UTF-8、写时未指定编码 |
| 文件多出空行 | Windows 换行符转换,加 newline='' |
FileNotFoundError | 相对路径基于工作目录,不是脚本目录 |
用脚本处理文件时,路径基准建议统一用 Path(__file__).parent,避免「在项目根目录能跑、换个目录就找不到文件」。
八、临时文件与原子写
需要「写得失败不留半成品」时,先写临时文件再 os.replace(原子替换):
import os, tempfile
fd, tmp = tempfile.mkstemp(dir='.')
with os.fdopen(fd, 'w', encoding='utf-8') as f:
f.write(content)
os.replace(tmp, 'target.txt')
直接覆盖写如果中途崩溃,目标文件会变成半截内容——配置文件尤其要避免这种情况。
小结
open 必写 encoding='utf-8';用 with 管理资源;大文件逐行迭代;JSON 用 ensure_ascii=False;路径用 pathlib;CSV 加 newline='';关键文件原子写。