有没有内存高效且快速的方式加载大JSON文件?

95 投票
11 回答
83639 浏览
提问于 2025-04-15 20:09

我有一些500MB的json文件。如果我用简单的json.load()一次性加载所有内容,会消耗很多内存。

有没有办法部分读取这个文件?如果它是一个文本文件,按行分隔的,我就可以逐行读取。我在寻找类似的办法。

11 个回答

18

问题不是每个文件太大,而是文件数量太多,导致它们在内存中累积。Python的垃圾回收机制应该没问题,除非你保留了一些不需要的引用。没有更多的信息,很难确切知道发生了什么,但你可以尝试以下几种方法:

  1. 将你的代码模块化。可以这样做:

    for json_file in list_of_files:
        process_file(json_file)
    

    如果你编写的 process_file() 函数不依赖于任何全局状态,也不改变任何全局状态,那么垃圾回收机制就能正常工作。

  2. 在不同的进程中处理每个文件。不要一次性解析所有的JSON文件,而是写一个程序只解析一个文件,然后通过一个shell脚本或另一个Python进程使用 subprocess.Popen 调用你的脚本,逐个传入文件。这样虽然不太优雅,但如果其他方法都不奏效,这样可以确保你不会在处理下一个文件时保留上一个文件的旧数据。

希望这些建议对你有帮助。

106

这个问题有一个重复的帖子,里面的答案更好。可以看看这个链接:https://stackoverflow.com/a/10382359/1623645,它推荐了一个叫做ijson的工具。

更新:

我试了一下,发现ijson处理JSON的方式就像SAX处理XML一样。举个例子,你可以这样做:

import ijson
for prefix, the_type, value in ijson.parse(open(json_file_name)):
    print(prefix, the_type, value)

这里的prefix是JSON树中的一个用点分隔的索引(如果你的键名里有点,那会发生什么呢?我想这对JavaScript来说也不好...),theType描述了一种类似SAX的事件,可能是'null', 'boolean', 'number', 'string', 'map_key', 'start_map', 'end_map', 'start_array', 'end_array'中的一种,而value是对象的值,如果the_type是像开始/结束一个映射或数组这样的事件,那么value就是None

这个项目有一些文档说明,但整体的文档不够。我不得不深入到ijson/common.py里去找我想要的东西。

3

更新

可以看看其他回答里的建议。

2010年的原始回答,现在已经过时

简单来说:不可以。

要正确地把一个json文件分割开,需要对json对象的结构有很深入的了解。

不过,如果你掌握了这些知识,就可以创建一个类似文件的对象,来包装这个json文件,并把它分成合适的部分。

比如说,如果你知道你的json文件是一个包含多个对象的数组,你可以写一个生成器,来包装这个json文件,并返回数组的各个部分。

你需要对字符串内容进行一些解析,才能正确地分割json文件。

我不太清楚是什么生成了你的json内容。如果可以的话,我建议你生成几个小一点的文件,而不是一个超大的文件。

撰写回答