因为open().read()或readlines()会一次性将整个文件加载进内存,导致MemoryError;应改用for line in f逐行迭代、分块读取(如read(chunk_size))或内存映射(mmap)等流式处理方式。

为什么用open()直接读大文件会爆内存
因为默认的 open(file).read() 会把整个文件一次性加载进内存,哪怕文件只有几百MB,也可能触发 MemoryError,尤其在内存受限或同时跑多个进程的环境里。这不是Python bug,是设计使然——它优先保证操作原子性和字符串处理便利性,不考虑流式场景。
常见错误现象:MemoryError 出现在调用 read()、readlines() 或 list(f) 后;任务管理器显示Python进程内存飙升到几GB;脚本在小文件上正常,换大文件就挂。
- 别用
read()或readlines()加载整文件,除非你明确知道文件大小远小于可用内存 - 避免
for line in f.readlines():—— 这等价于先全读进列表再遍历,白占内存 - 注意编码开销:UTF-8下中文字符可能占3字节,但Python内部用UCS-4存储(每个字符4字节),实际内存占用可能是文件体积的2–3倍
按行迭代是最简单有效的替代方案
Python 文件对象本身就是迭代器,for line in f: 是真正的逐行流式读取,内存只保留当前行,无论文件多大都稳定。
使用场景:日志分析、CSV预处理、文本清洗、逐行匹配正则等不需要随机访问的场合。
立即学习“Python免费学习笔记(深入)”;
- 确保用
with open(...)上下文管理,防止句柄泄漏 - 行尾换行符
\n会保留在line末尾,需要时用line.rstrip('\n')去掉(比strip()更精准,避免误删空格) - 如果某行超长(比如单行JSON或base64),仍可能触发单行内存溢出——这时得切块读,不是按行问题,而是数据结构问题
with open('huge.log', 'r', encoding='utf-8') as f:
for line in f: # 真·流式,内存恒定
if 'ERROR' in line:
print(line.rstrip('\n'))
需要跳过头部或按块处理时用readline()和read(size)
当不能简单按行处理(比如二进制文件、固定长度记录、跳过BOM/表头),就得控制读取粒度。readline() 比 for line in f 更灵活(可条件跳过),read(size) 则能精确控内存峰值。
参数差异:read(8192) 每次最多读8KB,返回 bytes(二进制模式)或 str(文本模式);readline() 读到下一个 \n 或 EOF,长度不确定但可控。
- 文本模式下慎用
read(size):可能把一个UTF-8字符从中间截断,导致解码错误;如需分块,建议用二进制模式 + 自己处理编码 - 用
readline()跳过前10行:for _ in range(10): f.readline(),比next()更直观 - 性能提示:频繁小尺寸
read(1)比read(8192)慢10倍以上,I/O系统调用开销主导
用mmap应对超大文件的随机访问需求
如果必须像操作内存数组一样随机读写(比如查找偏移量、二进制协议解析、内存映射数据库),mmap 是唯一合理选择——它让OS帮你做分页,Python只看到“好像全在内存”,实际按需加载。
注意:Windows上对大文件映射可能失败(默认用户空间限制2GB),Linux更友好;且 mmap 不解决编码问题,通常配合 bytes 操作。
- 必须用二进制模式打开文件:
open(file, 'rb'),再传给mmap.mmap() - 映射后不能用
.decode()整体转字符串,要用切片 +decode()局部处理,例如mm[100:200].decode('utf-8') - 映射对象要显式
close(),否则可能锁住文件或泄露资源(尤其在循环中反复映射时)
import mmap
with open('data.bin', 'rb') as f:
with mmap.mmap(f.fileno(), 0, access=mmap.ACCESS_READ) as mm:
header = mm[:8] # 快速读前8字节
# 后续可任意 mm[offset:offset+length] 访问
真正难的不是选哪种方法,而是判断「我到底需不需要整份数据」——多数报 MemoryError 的场景,其实只需要一次看一行、一块或一个字段。强行缓存只会掩盖设计问题。


















