
本文介绍如何正确处理以换行分隔的 json 对象(jsonl 格式)大文件,避免误用流式 json 解析器(如 ijson)导致的“trailing garbage”错误,并提供简洁、内存友好的标准库解决方案。
本文介绍如何正确处理以换行分隔的 json 对象(jsonl 格式)大文件,避免误用流式 json 解析器(如 ijson)导致的“trailing garbage”错误,并提供简洁、内存友好的标准库解决方案。
您遇到的 trailing garbage 错误并非代码逻辑问题,而是文件格式本质误解所致。您下载的 Kaggle 数据集(US Job Postings)并非一个合法的 JSON 文档,而是一个典型的 JSON Lines(JSONL)或 NDJSON(Newline-Delimited JSON) 文件:每行是一个独立、完整、可解析的 JSON 对象,但整份文件不符合 JSON 语法规范(例如,缺少外层数组或对象包裹,且多对象间无逗号分隔)。
因此,ijson(专为解析单个大型 JSON 文档设计,支持路径式流式提取如 "items.item")在此场景下并不适用——它试图将整个文件当作一个 JSON 实体解析,自然会在第一个对象结束后将第二行识别为非法的“尾部垃圾”。
✅ 正确做法是:逐行读取 + 逐行解析。这既轻量、高效,又天然适配 JSONL 格式,且完全无需额外依赖:
import json
# 推荐使用 UTF-8 编码(Kaggle 数据通常为 UTF-8),并显式指定 encoding 防止乱码
with open('file.json', 'r', encoding='utf-8') as f:
for line_num, line in enumerate(f, 1):
line = line.strip() # 去除首尾空白(含换行符),避免空行报错
if not line: # 跳过空行
continue
try:
obj = json.loads(line)
# ✅ 此时 obj 即为一个完整的字典,可直接处理
print(f"Processed item {line_num}: {obj.get('_id', {}).get('$oid', 'N/A')}")
except json.JSONDecodeError as e:
print(f"JSON decode error on line {line_num}: {e} | Content: {line[:100]}...")
? 关键注意事项:
- 不要用 'rb' 模式配合 json.loads():json.loads() 接收字符串,'rb' 返回 bytes,需先 .decode(),易出错;直接 'r' + encoding 更安全。
- 务必 strip() 和跳过空行:部分 JSONL 文件可能含空行或 BOM,引发解析异常。
- 加入异常捕获:3GB 文件中偶有格式瑕疵在所难免,健壮性至关重要。
- 性能提示:此方法内存占用恒定(≈ 单行大小),轻松处理数十 GB JSONL 文件;若需进一步加速,可结合 concurrent.futures 多进程解析(注意 I/O 瓶颈)。
? 总结:面对以换行为分隔的多 JSON 对象文件(JSONL/NDJSON),请放弃 ijson、json.load() 等面向单文档的工具,回归朴素而强大的“for line in file + json.loads(line)”范式——它简单、标准、可靠,且正是为此类场景而生。










