最可靠的大文件读取方式是open()配合for line in file,因其懒加载、内存占用低、不崩且可控;50GB日志实测稳定,无需调缓冲,须显式指定encoding(推荐utf-8-sig),二进制或定长记录可用read(size),随机访问才用mmap,多进程需预扫描换行符分片。

用 open() 配合 for line in file 是唯一靠谱起点
几十G的文件绝不能用 readlines() 或 read() 一次性加载进内存,否则直接 OOM。Python 默认的文件迭代器是懒加载、逐行读取、内存占用极低——这是最简单也最健壮的起点。
关键不是“怎么快”,而是“不崩”和“可控”。实测 50GB 日志文件在普通服务器上用该方式稳定跑数小时无压力。
- 不要手动调
buffering参数试图“优化”,默认 8KB 缓冲已足够;盲目加大(如设为 1MB)反而可能因系统页缓存竞争导致实际性能下降 - 确保用
encoding显式指定编码(如encoding='utf-8'),否则遇到非法字节序列时会抛UnicodeDecodeError中断整个流程 - 若文件含 BOM,
utf-8-sig比utf-8更稳妥,能自动剥离开头的\ufeff
按块读取(read(size))适合二进制处理或固定长度记录
当文件是纯二进制(如自定义日志格式、网络抓包 dump)或每条记录严格等长时,read(size) 比逐行更高效——跳过换行符查找开销,吞吐更高。
但注意:文本场景下它无法感知“行”边界,你得自己处理截断、拼接、换行符残留等问题,复杂度陡增。
立即学习“Python免费学习笔记(深入)”;
- 例如读 64KB 块:
chunk = f.read(65536),返回bytes,需自行.decode()(并捕获UnicodeDecodeError) - 若记录跨块(如某行被切在两块中间),必须保留末尾不完整部分,下一次读取前先拼回去,逻辑易出错
- Windows 下
\r\n换行在二进制模式下不会被转换,需手动识别,而文本模式自动处理这点
用 mmap 加速随机访问,但别误用于顺序遍历
mmap 的价值在于“像操作内存一样访问大文件”,适合需要反复跳转、定位某偏移或做字符串搜索(如 mmap.find(b'ERROR'))的场景。它不减少 I/O 总量,也不加速顺序读取。
对几十G文本做全量扫描时,mmap 反而更容易触发缺页中断抖动,且占用虚拟内存地址空间,在 32 位 Python 或内存受限容器中可能失败。
- 仅当你要做类似 “找第 N 个匹配项的位置” 或 “从末尾往前找最后 10 行” 这类非线性操作时才考虑
mmap - 务必用
access=mmap.ACCESS_READ,写入或复制会触发 COW,瞬间吃光物理内存 - 关闭文件前必须先
del mmap_obj或显式mmap_obj.close(),否则可能卡住文件句柄
多进程分片处理需手动切分,linecache 不适用
想用 multiprocessing 加速?别指望 linecache.getline() 或 seek() 到第 N 行——文本文件没有索引,行长度不确定,“第 100 万行” 的字节偏移只能靠预扫描获得。
可行做法是:先单进程扫一遍,记录所有换行符位置(或每隔 100MB 记一个偏移),再把文件按字节范围分给多个子进程,每个进程用 open(..., 'rb') 定位后,跳过首行不完整部分,再逐行处理。
- 切分点必须落在
\n后面,否则某一行被劈成两半,分别由两个进程解析,结果错乱 - 子进程内仍推荐用文本模式 +
for line in file,而非自己解析bytes,可复用 Python 的编码容错机制 - 避免用
file.tell()在文本模式下获取位置——Windows 下不可靠,Unix 下也未必等于字节偏移
真正难的从来不是“怎么读”,而是“怎么保证每一行都被完整、正确、不重复、不遗漏地送进你的解析逻辑”。缓冲区、编码异常、换行符兼容性、进程间边界对齐——这些细节漏掉一个,结果就不可信。


















