我有一些500MB的json文件。 如果我使用“trivial”json.load一次加载其内容,它将消耗大量内存。
有没有办法部分阅读文件?如果它是一个文本,行分隔文件,我将能够遍历这些行。我正在寻找它的类比。
有什么建议吗? 感谢
答案 0 :(得分:68)
这个问题的副本有更好的答案。请参阅https://stackoverflow.com/a/10382359/1623645,其中建议ijson。
<强>更新强>
我试了一下,ijson是JSON对XML的SAX。例如,您可以这样做:
import ijson
for prefix, the_type, value in ijson.parse(open(json_file_name)):
print prefix, the_type, value
其中prefix
是JSON树中以点分隔的索引(如果你的键名中有点,会发生什么?我想这对Javascript也不好......),{{1} }描述类似SAX的事件,theType
之一,'null', 'boolean', 'number', 'string', 'map_key', 'start_map', 'end_map', 'start_array', 'end_array'
是对象的值,如果value
是一个事件,如开始/结束地图/ None
阵列。
该项目有一些文档字符串,但没有足够的全局文档。我不得不深入the_type
找到我想要的东西。
答案 1 :(得分:14)
所以问题不是每个文件都太大,而是它们太多了,而且它们似乎在内存中加起来。 Python的垃圾收集器应该没问题,除非你保留你不需要的引用。如果没有任何进一步的信息,很难确切地说出发生了什么,但有些事情可以尝试:
模块化您的代码。做类似的事情:
for json_file in list_of_files:
process_file(json_file)
如果以不依赖于任何全局状态的方式编写process_file()
,则不会
改变任何全局状态,垃圾收集器应该能够完成它的工作。
在单独的流程中处理每个文件。不要一次解析所有JSON文件,而是写一个
程序只解析一个,并从shell脚本或另一个python传递每个
通过subprocess.Popen
调用脚本的过程。这有点不太优雅,但如果
没有其他工作,它将确保你没有坚持从一个文件到陈旧的数据
下。
希望这有帮助。
答案 2 :(得分:8)
是
您可以使用我编写的 jsonstreamer 类似SAX的推送解析器,它允许您解析任意大小的块,您可以get it here并查看README以获取示例。它很快,因为它使用了&#39; C&#39; yajl图书馆。
答案 3 :(得分:3)
在提到内存不足时,我必须质疑你是否真的在管理内存。在尝试阅读新对象之前,您是否使用“del”关键字删除旧对象?如果你删除它,Python应该永远不会在内存中保留一些内容。
答案 4 :(得分:3)
“垃圾收集器应释放内存”
正确。
既然没有,那就别错了。通常,无限内存增长的问题是全局变量。
删除所有全局变量。
将所有模块级代码转换为更小的函数。
答案 5 :(得分:2)
另一个想法是尝试将其加载到像MongoDB这样的文档存储数据库中。 它很好地处理了大量的JSON。虽然加载JSON可能遇到同样的问题 - 通过一次加载一个文件来避免问题。
如果path适合你,那么你可以通过他们的客户端与JSON数据交互,并且可能不必将整个blob保存在内存中
答案 6 :(得分:1)
除了@codeape
我会尝试编写一个自定义的json解析器来帮助你弄清楚你正在处理的JSON blob的结构。仅打印出关键名称等。制作一个分层树并决定(自己)如何将其分块。这样你就可以做@codeape建议 - 将文件分成更小的块等等
答案 7 :(得分:1)
可以使用ijson完成。 Jim Pivarski在上面的答案中已经很好地解释了ijson的工作原理。下面的代码将读取一个文件,并从列表中打印每个json。例如,文件内容如下
[{"name": "rantidine", "drug": {"type": "tablet", "content_type": "solid"}},
{"name": "nicip", "drug": {"type": "capsule", "content_type": "solid"}}]
您可以使用以下方法打印数组的每个元素
def extract_json(filename):
with open(filename, 'rb') as input_file:
jsonobj = ijson.items(input_file, 'item')
jsons = (o for o in jsonobj)
for j in jsons:
print(j)
注意:“ item”是ijson给出的默认前缀。
如果您只想根据条件访问特定的json,可以按照以下方式进行操作。
def extract_tabtype(filename):
with open(filename, 'rb') as input_file:
objects = ijson.items(input_file, 'item.drugs')
tabtype = (o for o in objects if o['type'] == 'tablet')
for prop in tabtype:
print(prop)
这将仅打印类型为平板电脑的json。
答案 8 :(得分:0)
您可以将 JSON 文件解析为 CSV 文件并逐行解析:
Person::addSelect('last_visit_id', Visit::select('id')
->whereColumn('person_id', 'persons.id')
->latest()
->limit(1)
)
->where('last_visit_id', 1)
->get();
答案 9 :(得分:0)
所以简单地使用 json.load() 会花费很多时间。相反,您可以使用键值对将 json 数据逐行加载到字典中,并将该字典附加到最终字典中,并将其转换为 Pandas DataFrame,这将有助于您进一步分析
def get_data():
with open('Your_json_file_name', 'r') as f:
for line in f:
yield line
data = get_data()
data_dict = {}
each = {}
for line in data:
each = {}
# k and v are the key and value pair
for k, v in json.loads(line).items():
#print(f'{k}: {v}')
each[f'{k}'] = f'{v}'
data_dict[i] = each
Data = pd.DataFrame(data_dict)
#Data will give you the dictionary data in dataFrame (table format) but it will
#be in transposed form , so will then finally transpose the dataframe as ->
Data_1 = Data.T
答案 10 :(得分:-1)
简答:不。
正确划分json文件会对json对象图有深入了解才能正确。
但是,如果你有这方面的知识,那么你可以实现一个类似文件的对象,它包装json文件并吐出适当的块。
例如,如果您知道您的json文件是单个对象数组,则可以创建一个包装json文件并返回数组块的生成器。
您必须进行一些字符串内容解析才能正确获取json文件的分块。
我不知道是什么生成了你的json内容。如果可能的话,我会考虑生成一些可管理的文件,而不是一个大文件。