json.loads不适合解析超大json文件,因其需将整个文件读入内存为字符串再解析,易触发memoryerror;应改用json.load(文件对象)或更优的ijson流式提取、json lines逐行处理。

json.loads 本身不能用于解析超大 JSON 文件——它只接受字符串,而把整个 GB 级文件读成字符串再传给 json.loads,等于主动触发 MemoryError。真正该用的是 json.load(带文件对象),但即便如此,它仍是一次性加载全部结构到内存,不解决根本问题。
为什么 json.loads 不适合大文件
你看到的“加速”误区,往往来自混淆了输入类型:json.loads(s) 必须先把文件 f.read() 成完整字符串 s,这一步就已吃光内存;而 json.load(f) 虽省去中间字符串拷贝,但仍需构建完整嵌套对象树。两者在超大文件场景下都不可行。
常见错误现象:
- 调用
json.loads(f.read())后直接卡死或报MemoryError - 误以为换用
ujson或orjson就能“流式处理”——它们只是更快的loads替代品,不改变一次性加载语义
真正有效的替代方案:ijson + 显式路径提取
当 JSON 结构固定(如顶层是 {"items": [...]}),且你只需要遍历数组元素时,ijson.items() 是最轻量、最可控的选择。它不构建整棵树,只按需 yield 每个匹配项。
实操建议:
- 安装:
pip install ijson - 路径写法必须精确:
ijson.items(f, 'items.item')表示从items数组中逐个取item;若顶层是数组,用ijson.parse(f)配合状态机,或改用ijson.kvitems(f, '') - 务必以二进制模式打开文件:
open('data.json', 'rb'),避免编码干扰和换行符歧义 - 不要在循环里反复调用
ijson.items(f, ...)——文件指针不会自动重置,应重新打开或用seek(0)
JSON Lines(.jsonl)格式优先考虑逐行 + json.loads
如果你的“超大 JSON 文件”其实是每行一个独立 JSON 对象(常见于日志、MongoDB 导出、Kaggle 数据集),那根本不需要 ijson。强行用它反而会报 trailing garbage 错误。
正确做法:
- 逐行读取,
line.strip()去除空白和 BOM - 对每行单独调用
json.loads(line)—— 这时loads是安全的,因为单行数据小 - 必须加
try/except json.JSONDecodeError,真实数据常含脏行 - 编码显式指定
encoding='utf-8',否则遇到非 UTF-8 字节会崩在第一行
容易被忽略的底层细节
性能差异常藏在 IO 和编码层:
-
json.load(f)比json.loads(f.read())省一次内存拷贝,但两者都扛不住 GB 级别; - 用
orjson替换json只对loads/dumps有效,且要求输入是bytes,不兼容文本模式文件对象; - 如果文件是压缩的(如
.json.gz),直接用gzip.open(..., 'rt', encoding='utf-8')配合逐行读取,比解压后再处理更省内存; - 所有流式方案都默认放弃随机访问能力——你不能再回头查第 1000 条,这点在设计 pipeline 时就得接受。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











