跳到正文
效率工具实用笔记关于本站
表格与数据

多条JSON记录怎样逐行读取:一次只处理一行

约 7 分钟阅读AI 辅助整理

自己约定每行一个完整JSON对象,用文件迭代配合json.loads逐行解析,出错时报告行号和原因。json.load不支持多个拼接对象,所以不要用它读这种文件。

本文要点

  1. json.load(fp)的作用是把一个文件对象里的JSON文档反序列化成Python对象

  2. 下面这段代码只读练习文件,不修改原文件

  3. 对逐行读取来说,你已经在外面用enumerate记了行号,所以报错时把外层行号和e.colno一起打印,就能定位到具体位置

多条JSON记录怎样逐行读取:一次只处理一行的知识要点示意图
依据已审核正文摘取要点制作的示意图,非实物照片;请结合正文中的条件与范围阅读。要点摘自已审核正文;完整条件与说明见正文。查看高清图解 ↗
本文目录

先约定文件长什么样:每行一个完整对象

如果你手里有一个文件,里面存了很多条JSON记录,想一条一条处理,先要解决格式问题。json模块的文档明确说,JSON不是分帧协议,用同一个文件对象反复调用dump()会得到无效的JSON文件。也就是说,一个文件里堆多个对象,json.load读不了。

所以本文只处理一种你自己约定的格式:每行一个完整JSON对象,行与行之间用换行分开。例如练习文件exercise/records.jsonl里写三行,每行都是一个独立对象。这种格式不是JSON标准规定的,是你和文件之间的约定,读的时候也必须按这个约定来。

适用情境:你有一个自建的多行JSON文件,想逐行读取、逐行处理,并且希望某一行出错时能知道是第几行。本文所有文件都是练习用的自建示例,不涉及真实业务数据。动手前先复制一份文件作为练习副本。

为什么不用json.load:它要的是整个文件一个文档

json.load(fp)的作用是把一个文件对象里的JSON文档反序列化成Python对象。它期望整个文件是一个完整的JSON文档,而不是多行拼接的多个对象。

如果你把三行对象直接交给json.load,解析会在第一个对象结束后遇到多余内容,抛出JSONDecodeError。文档里也提到,JSON不是分帧协议,多次dump()到同一个文件会得到无效JSON。所以对这种逐行格式,正确做法是不要用json.load,而是自己按行读、对每一行单独调用json.loads。

json.loads(s)接收一个字符串,把它解析成Python对象。它一次只处理一个文档,正好对应你的一行。这样每行的成败互不影响:某一行格式错了,只影响那一行,后面的行还能继续读。

逐行读取的写法:文件迭代加json.loads

下面这段代码只读练习文件,不修改原文件。路径用明确的练习相对路径。

import json

path = "exercise/records.jsonl"

with open(path, "r", encoding="utf-8") as f:
    for lineno, line in enumerate(f, start=1):
        line = line.strip()
        if not line:
            continue
        try:
            record = json.loads(line)
        except json.JSONDecodeError as e:
            print(f"第{lineno}行解析失败:{e.msg}(列{e.colno})")
            continue
        print(f"第{lineno}行:{record}")

几个要点。第一,用for line in f直接迭代文件对象,一次拿到一行,不需要先把整个文件读进内存。第二,用enumerate从1开始编号,方便报错时定位。第三,先strip()去掉行尾换行和空白,空行跳过。第四,每行单独json.loads,放在try里。

这样处理的结果是:每行独立解析,成功就得到一条Python对象,失败就报告行号,继续下一行。

逐行错误报告:用JSONDecodeError的字段定位

json.loads解析失败时抛出json.JSONDecodeError,它是ValueError的子类,带有几个有用的属性:msg是未格式化的错误消息,doc是被解析的文档,pos是解析失败处的起始索引,lineno和colno是对应pos的行号和列号。

对逐行读取来说,你已经在外面用enumerate记了行号,所以报错时把外层行号和e.colno一起打印,就能定位到具体位置。例如某行少了一个右花括号,报错会指出期望的位置。

注意:这里的lineno是这一行内部的相对行号,因为你每次只把一行交给loads,所以它通常是1。真正区分是哪条记录,靠的是外层enumerate的行号。把两者分开写清楚,排查时不会混。

一个建议:报错时把出错的那一行原文也打印出来(可以截断长度),这样你能直接看到问题字符。

自建示例:三行里故意放一行错的

假设练习文件exercise/records.jsonl内容如下(自建示例):

{"id": 1, "name": "示例甲"}
{"id": 2, "name": "示例乙"
{"id": 3, "name": "示例丙"}

第二行少了一个右花括号。按上面的代码运行,预期输出是:第1行和第3行正常解析成字典并打印;第2行进入except分支,打印“第2行解析失败”以及错误消息和列号。

这个示例说明两点。第一,一行出错不会中断整个文件,后面的行照常处理。第二,报错信息里的行号来自外层enumerate,列号来自JSONDecodeError,两者配合能定位。

以上是编辑设计的练习示例和预期结果,不是对某个真实文件的测试记录。

检查结果与常见错误排查

读完文件后,可以按下面几条检查结果是否符合预期。

1. 行数对不对:成功解析的条数加上报错条数,应该等于文件里的非空行数。如果对不上,检查是不是有行被strip()后当成空行跳过了。

2. 报错行是不是真的有问题:把报错行原文拿出来,用python -m json单独校验这一行。文档说明json模块可以作为脚本运行来校验和美化JSON,命令行选项里有--json-lines,可以按行解析每个输入行作为独立JSON对象。

3. 常见错误:单引号代替双引号、尾随逗号、缺右括号、行内混入多余字符。这些都会让json.loads抛JSONDecodeError。

4. 编码问题:打开文件时指定encoding="utf-8"。文档指出JSON要求用UTF-8、UTF-16或UTF-32表示,UTF-8是推荐默认值。如果文件不是这些编码,读取时可能抛UnicodeDecodeError。

排查顺序建议:先确认行号,再看该行原文,再用命令行工具单独校验这一行。

使用限制与参考资料

本文只处理一种约定格式:每行一个完整JSON对象。它不适用于一个文件里放一个JSON数组的情况,也不适用于多个对象拼接但没有换行分隔的情况。后两种应该用json.load整体读取,不属于本文范围。

几个限制要说明。第一,逐行格式是你自己约定的,不是JSON标准的一部分,所以别人给的文件不一定符合这个约定,读之前先确认。第二,json.loads一次只解析一个文档,如果某一行里塞了两个对象,它会在第一个对象后遇到多余数据而报错。第三,解析不可信来源的JSON时要谨慎,文档提醒恶意JSON字符串可能消耗大量CPU和内存,建议限制要解析的数据大小。

本文示例只操作自建练习文件,不执行删除或系统修改。所有代码和文件名都是练习用的自建示例。

参考资料:Python官方json模块文档,https://docs.python.org/3/library/json.html。

观看知识要点视频 18 秒

18秒无旁白要点导览,文字来自已审核正文;完整步骤、条件及参考资料见下方。

参考资料

以下原始资料提供本文依据;请结合日期、适用条件和产品说明查阅。

请结合原始资料的适用条件阅读。

返回表格与数据 →