报错是这么冒出来的。清洗后的 JSONL 逐行解析,跑到第 4000 多行抛 json.decoder.JSONDecodeError: Expecting value: line 1 column 1,把那一行贴到编辑器里看不出任何异常。
中间走过弯路,把「字段里换行没转义,一条记录被拆成两行」当成主因,改完现象没变。
真正麻烦的地方在于,几个原因的现象几乎一样。行首混入全角空格,解析器不认作空白。也可能是清洗脚本写入时用了 utf-8-sig,BOM 粘在第一行。
少数情况下是空行与只有注记的行没有被过滤。
第一件事是用 repr 打印出错行确认隐藏字符
python -c "l=open('data.jsonl','rb').read().splitlines()[4012];print(repr(l[:40]))"
接着统计全角空格与 BOM 出现的位置
python -c "d=open('data.jsonl','rb').read();print(d.count('\u3000'.encode()), d[:3])"
再往下逐行清洗空白后重新写出
python -c "import json;[json.dumps(json.loads(l.replace('\u3000',' ').strip()),ensure_ascii=False) for l in open('data.jsonl',encoding='utf-8-sig') if l.strip()]"
剩下的就是加一行一校验的导入脚本记录坏行号
python load_jsonl.py --input data.jsonl --strict --report bad.json
恢复的判定其实很直接。全量导入成功,坏行清单为空,首行没有 BOM,字段里的换行都以转义形式存在。
容易忽略的是,看不见的字符只能靠 repr 或字节统计暴露,JSONL 处理要在入口就做去空白与 BOM 清理,别等到解析报错再回头找。线上能稳定复现的问题都不算难,难的是只在大流量下才出现的那种。
