正确路径是按行解析JSON Lines或用ijson流式解析单个嵌套JSON:前者用json.loads()逐行处理并加strip/utf-8/异常捕获,后者用ijson.items(f, 'path')配rb模式;混杂结构才需ijson.parse()事件驱动。

直接用 json.loads() 解超大 JSON 流,基本等于主动申请内存溢出。真正可行的路径只有两条:按行解析(JSON Lines)或流式事件驱动(ijson),选错就卡死。
遇到 "trailing garbage" 错误,说明你误用了 ijson 解析 JSON Lines
这个错误不是你的代码写错了,而是文件格式和解析器不匹配。ijson 设计用来解析单个嵌套 JSON 文档(比如 [{...}, {...}]),而 JSON Lines 是每行一个独立 JSON 对象,没有顶层容器。ijson 会把第一行当完整文档解析完,第二行开头就报 “trailing garbage”。
- ✅ 正确做法:放弃
ijson.items(),改用内置json.loads()配合逐行读取 - ⚠️ 必须
line.strip()—— 换行符、BOM、空格都会让json.loads()直接抛JSONDecodeError - ⚠️ 必须显式指定
encoding="utf-8"—— 真实数据集常见 Latin-1 或带 BOM 的 UTF-8,不指定就崩 - ⚠️ 必须包
try/except json.JSONDecodeError—— 大文件总有半截行、转义错误、控制字符,不捕获就中断
处理标准大数组([{},{},...])时,ijson.items() 是最简稳解
别碰 ijson.parse() 做状态机——容易漏事件、难调试、维护成本高。95% 的场景用 ijson.items(f, "item") 就够了,它内部已做缓冲优化,内存只驻留当前 item。
- 路径字符串要写对:
"data.item"表示顶层{"data": [...]}里的数组元素;"item"表示顶层就是数组[...] - 文件必须用
"rb"模式打开 ——ijson底层是字节流解析,传文本流会报TypeError - 不要在循环里反复调
ijson.items()—— 每次都重开文件、重置解析器,IO 开销翻倍 - 如果字段嵌套深(如
records.data.user.name),优先用ijson.items(f, "records.data.user")跳过无关层级,比ijson.parse()+ 手动匹配 prefix 更快更稳
想提速?换解析器,但别乱换
ujson 和 orjson 对单次 loads() 有 2–5 倍加速,但前提是:你已经在用逐行或 ijson.items() 控制住了内存。它们不能解决“加载整个文件”的问题,只加速“解析这一行”或“解析这一个 item”。
立即学习“Python免费学习笔记(深入)”;
-
orjson不支持object_hook,如果你依赖datetime自动反序列化,得自己后处理 -
ujson对中文支持弱,某些 Unicode 字符会静默失败,生产环境慎用 - 替换方式很简单:把
import json换成import ujson as json,其余代码不动(ijson不走这里) - 真正瓶颈在 IO 时,加
mmap比换解析器更有效 —— 特别是 SSD 上随机读大文件,mmap+ujson组合可再降 20% 耗时
最容易被忽略的一点:所有流式方案都依赖“结构可知”。如果你拿到的文件既不是 JSON Lines,也不是标准数组,而是混着对象、数组、原始值的嵌套怪物,那 ijson.parse() + 状态机是唯一出路——但这时你应该先确认,是不是上游导出逻辑本身就该修正。



















