jsonlines库比手动解析更稳定,专为JSONL设计,自动处理BOM、换行符、末尾逗号;去重需用frozenset或sort_keys序列化;合并用itertools.chain流式处理;写入须设ensure_ascii=False和UTF-8编码。

用 jsonlines 读取多个文件比手动 open() + json.loads() 更稳
JSONL(每行一个 JSON 对象)不是标准 JSON 数组,直接用 json.load() 会报 JSONDecodeError: Extra data。别硬扛,装个轻量库:pip install jsonlines。它专为逐行解析设计,自动跳过空行、处理编码异常,比手写 for line in f 少踩三类坑:BOM 头、换行符混用、末尾逗号误判。
常见错误是把所有文件一次性 read() 再切分,结果遇到带换行的字符串字段就崩。正确姿势是让 jsonlines.Reader 按行流式读——内存友好,也避免单行超长导致 OOM。
用 frozenset 或 json.dumps(obj, sort_keys=True) 做去重键
JSONL 中对象顺序不保证,{"a":1,"b":2} 和 {"b":2,"a":1} 是同一逻辑对象,但 Python 字典默认不等价。不能直接塞进 set。
- 简单场景(字段少、无嵌套):用
frozenset(obj.items())当 hash key,快且省内存 - 通用场景(含 list、嵌套 dict):必须序列化再哈希,推荐
json.dumps(obj, sort_keys=True, separators=(',', ':'))——sort_keys确保键序一致,separators去掉空格,否则相同内容因格式差异被当成不同项 - 注意:含浮点数时,
json.dumps默认保留小数位,若原始数据精度不一致(如3.0vs3.00),得先统一转int或用round(x, N)
合并时用生成器 + itertools.chain 避免全量加载
假设你有 50 个 200MB 的 JSONL 文件,全读进内存再去重,Python 很可能直接被系统 kill。得边读边去重。
立即学习“Python免费学习笔记(深入)”;
实操建议:
- 用
itertools.chain.from_iterable( (reader.iter() for reader in readers) )把多个文件流拼成单个迭代器,不缓存中间结果 - 维护一个
seen = set(),每次拿到新对象,算出它的去重 key,检查是否已存在;不存在才 yield 出去 - 关键细节:key 计算尽量轻量,别在循环里反复调
json.dumps;可提前编译正则或缓存常用子结构
写入目标文件前务必加 ensure_ascii=False
中文、emoji 等非 ASCII 字符默认被转成 \uXXXX,导致文件体积翻倍、可读性归零。很多人合并完发现“文字变乱码”,其实是没关这个开关。
写入时用 jsonlines.Writer 并显式传参:Writer(output_file, ensure_ascii=False)。如果手写 print(json.dumps(...), file=f),也必须加 ensure_ascii=False,否则 UTF-8 字节被二次编码。
另外,别用 open(..., 'w') 而不用 encoding='utf-8' ——Windows 下默认是 cp1252,中文直接报 UnicodeEncodeError。
去重逻辑本身不难,真正的复杂点在于:JSON 结构是否规范(null 字段?nan 值?)、文件编码是否混杂(UTF-8 with BOM / GBK)、以及内存水位控制——尤其当某一行 JSON 实际长达几十 MB 时,jsonlines 的默认缓冲区可能吃满。这些细节不提前 check,跑一半失败,日志里只有一行 MemoryError。


















