直接用 open().readlines() 会触发 MemoryError,因其一次性将整个文件加载进内存生成列表;真正高效的是 for line in open(),它利用文件迭代器按需读取,每次仅加载一行,实现常数内存占用。

直接用 open().readlines() 会触发 MemoryError,尤其在内存 ≤8GB 的机器上处理 5GB 日志时几乎必现;真正高效的核心不是“快”,而是“常数内存占用 + 可控数据边界”。
为什么 for line in open() 是最稳妥的起点
文件对象天然支持迭代器协议,for line in f 底层调用 f.__next__(),每次只加载一行进内存,不缓存、不预读、不构建列表。它比显式写 f.readline() 更简洁,也比 f.readlines() 安全得多。
- 必须搭配
with语句,否则异常时文件句柄不释放 - 若文件含 BOM 或混合编码,推荐
encoding="utf-8-sig"自动剥离 BOM - 单行超长(如 base64 字段占 200MB)仍会 OOM——这不是读取方式问题,是数据格式本身越界
- 跳过空行或注释行,直接写
if not line.strip() or line.startswith("#"): continue,别提前filter()
按字节分块读取(rb 模式)适合什么场景
当文件无规律换行、含二进制垃圾、或某几行极长导致按行卡死时,read(size) 是更底层可控的选择。关键不是“快”,而是能硬性限制单次内存峰值。
- 务必用
mode='rb'打开,避免编码解析干扰和中断风险 -
size建议设为65536(2¹⁶)或1048576(1MB),对多数文件系统更友好 - UTF-8 中文可能被截断:读完一块后,检查末尾 1~3 字节是否构成完整字符,常用策略是预留末尾字节与下一块拼接再解码
- 不要用
io.TextIOWrapper包装来“自动处理边界”——它会丧失字节数精确控制能力
什么时候该考虑 mmap
mmap 不省物理内存,只是把文件映射成虚拟地址空间;它适合随机查找、多进程共享访问、或需反复回溯的场景,但不适合流式顺序处理。
立即学习“Python免费学习笔记(深入)”;
- 真要压内存,
mmap反而容易触发 swap,RSS 占用不降反升 - 只读场景可用
mmap.ACCESS_READ,写入需'r+b'模式打开原文件 - 无法跨平台处理换行符(
\r\n在 mmap 中就是两个字节,不会自动归一) - 若只是想“快速 grep 某个字符串”,
mmap+find()比逐行扫描快一个数量级
最容易被忽略的是:没有银弹。按行读取在绝大多数日志/CSV 场景下已足够;按块读取解决的是“格式不可信”问题;mmap 解决的是“随机访问”问题。选错前提,再优化也白搭。


















