Python:读取和写入巨大的 Json 文件
Python: Reading and Writing HUGE Json files
我是 python 的新手。如果我没有以 pythonic 方式提问,请原谅。
我的要求如下:
我需要编写python代码来实现这个需求。
将读取 60 json 个文件作为输入。每个文件大约 150 GB。
所有 60 个 json 文件的示例结构如下所示。请注意每个文件将只有一个 json 对象。每个文件的巨大尺寸是因为那个巨大的 json 对象中包含的“array_element”数组的数量和大小。
{
"string_1":"abc",
"string_1":"abc",
"string_1":"abc",
"string_1":"abc",
"string_1":"abc",
"string_1":"abc",
“array_element”:[]
}
转换逻辑很简单。我需要合并所有 60 个文件中的所有 array_element,并将其写入一个巨大的 json 文件。这几乎是 150GB X 60 将是输出 json 文件的大小。
我请求您帮助的问题:
阅读:计划使用“ijson”模块的ijson.items(file_object,“array_element”)。您能否告诉我 ijson.items 是否会从 json 文件中的“array_element”数组一次“产生”(即不将整个文件加载到内存中)一项?我不认为 json.load 是一个选项,因为我们无法在内存中保存如此庞大的字典。
对于写作:我打算使用ijson.item读取每个项目,然后json.dumps进行“编码”,然后使用[=68=将其写入文件] 并且不使用 json.dump,因为我无法在内存中使用如此庞大的字典来使用 json.dump。如果需要在下面显示的代码中应用 f.flush() ,您能否告诉我?据我了解,内部缓冲区已满时会自动刷新,并且内部缓冲区的大小是恒定的,不会动态增长到内存过载的程度?请告诉我
对于上面提到的增量读取和写入巨大的 json 文件,是否有更好的方法?
上述读写逻辑的代码片段:
for input_file in input_files:
with open("input_file.json", "r") as f:
objects = ijson.items(f, "array_element")
for item in objects:
str = json.dumps(item, indent=2)
with open("output.json", "a") as f:
f.write(str)
f.write(",\n")
f.flush()
with open("output.json", "a") as f:
f.seek(0,2)
f.truncate(f.tell() - 1)
f.write("]\n}")
希望我问清楚了我的问题。提前致谢!!
以下程序假设输入文件的格式足够可预测,可以跳过 JSON 解析以提高性能。
根据您的描述,我的假设是:
- 所有文件都有相同的编码。
- 所有文件在开头的某处都有一个位置,可以找到
"array_element":[
,之后文件的“有趣部分”开始
- 所有文件在末尾某处都有一个位置,
]}
标记“有趣部分”的结尾
- 所有“有趣的部分”都可以用逗号连接并且仍然有效JSON
当所有这些点都为真时,连接预定义的 header 片段、相应的文件范围和页脚片段将生成一个大的有效 JSON 文件。
import re
import mmap
head_pattern = re.compile(br'"array_element"\s*:\s*\[\s*', re.S)
tail_pattern = re.compile(br'\s*\]\s*\}\s*$', re.S)
input_files = ['sample1.json', 'sample2.json']
with open('result.json', "wb") as result:
head_bytes = 500
tail_bytes = 50
chunk_bytes = 16 * 1024
result.write(b'{"JSON": "fragment", "array_element": [\n')
for input_file in input_files:
print(input_file)
with open(input_file, "r+b") as f:
mm = mmap.mmap(f.fileno(), 0)
start = head_pattern.search(mm[:head_bytes])
end = tail_pattern.search(mm[-tail_bytes:])
if not (start and end):
print('unexpected file format')
break
start_pos = start.span()[1]
end_pos = mm.size() - end.span()[1] + end.span()[0]
if input_files.index(input_file) > 0:
result.write(b',\n')
pos = start_pos
mm.seek(pos)
while True:
if pos + chunk_bytes >= end_pos:
result.write(mm.read(end_pos - pos))
break
else:
result.write(mm.read(chunk_bytes))
pos += chunk_bytes
result.write(b']\n}')
如果文件格式是 100% 可预测的,您可以丢弃正则表达式并使用 mm[:head_bytes].index(b'...')
等进行 start/end 位置算法。
我是 python 的新手。如果我没有以 pythonic 方式提问,请原谅。
我的要求如下:
我需要编写python代码来实现这个需求。
将读取 60 json 个文件作为输入。每个文件大约 150 GB。
所有 60 个 json 文件的示例结构如下所示。请注意每个文件将只有一个 json 对象。每个文件的巨大尺寸是因为那个巨大的 json 对象中包含的“array_element”数组的数量和大小。
{ "string_1":"abc", "string_1":"abc", "string_1":"abc", "string_1":"abc", "string_1":"abc", "string_1":"abc", “array_element”:[] }
转换逻辑很简单。我需要合并所有 60 个文件中的所有 array_element,并将其写入一个巨大的 json 文件。这几乎是 150GB X 60 将是输出 json 文件的大小。
我请求您帮助的问题:
阅读:计划使用“ijson”模块的ijson.items(file_object,“array_element”)。您能否告诉我 ijson.items 是否会从 json 文件中的“array_element”数组一次“产生”(即不将整个文件加载到内存中)一项?我不认为 json.load 是一个选项,因为我们无法在内存中保存如此庞大的字典。
对于写作:我打算使用ijson.item读取每个项目,然后json.dumps进行“编码”,然后使用[=68=将其写入文件] 并且不使用 json.dump,因为我无法在内存中使用如此庞大的字典来使用 json.dump。如果需要在下面显示的代码中应用 f.flush() ,您能否告诉我?据我了解,内部缓冲区已满时会自动刷新,并且内部缓冲区的大小是恒定的,不会动态增长到内存过载的程度?请告诉我
对于上面提到的增量读取和写入巨大的 json 文件,是否有更好的方法?
上述读写逻辑的代码片段:
for input_file in input_files:
with open("input_file.json", "r") as f:
objects = ijson.items(f, "array_element")
for item in objects:
str = json.dumps(item, indent=2)
with open("output.json", "a") as f:
f.write(str)
f.write(",\n")
f.flush()
with open("output.json", "a") as f:
f.seek(0,2)
f.truncate(f.tell() - 1)
f.write("]\n}")
希望我问清楚了我的问题。提前致谢!!
以下程序假设输入文件的格式足够可预测,可以跳过 JSON 解析以提高性能。
根据您的描述,我的假设是:
- 所有文件都有相同的编码。
- 所有文件在开头的某处都有一个位置,可以找到
"array_element":[
,之后文件的“有趣部分”开始 - 所有文件在末尾某处都有一个位置,
]}
标记“有趣部分”的结尾 - 所有“有趣的部分”都可以用逗号连接并且仍然有效JSON
当所有这些点都为真时,连接预定义的 header 片段、相应的文件范围和页脚片段将生成一个大的有效 JSON 文件。
import re
import mmap
head_pattern = re.compile(br'"array_element"\s*:\s*\[\s*', re.S)
tail_pattern = re.compile(br'\s*\]\s*\}\s*$', re.S)
input_files = ['sample1.json', 'sample2.json']
with open('result.json', "wb") as result:
head_bytes = 500
tail_bytes = 50
chunk_bytes = 16 * 1024
result.write(b'{"JSON": "fragment", "array_element": [\n')
for input_file in input_files:
print(input_file)
with open(input_file, "r+b") as f:
mm = mmap.mmap(f.fileno(), 0)
start = head_pattern.search(mm[:head_bytes])
end = tail_pattern.search(mm[-tail_bytes:])
if not (start and end):
print('unexpected file format')
break
start_pos = start.span()[1]
end_pos = mm.size() - end.span()[1] + end.span()[0]
if input_files.index(input_file) > 0:
result.write(b',\n')
pos = start_pos
mm.seek(pos)
while True:
if pos + chunk_bytes >= end_pos:
result.write(mm.read(end_pos - pos))
break
else:
result.write(mm.read(chunk_bytes))
pos += chunk_bytes
result.write(b']\n}')
如果文件格式是 100% 可预测的,您可以丢弃正则表达式并使用 mm[:head_bytes].index(b'...')
等进行 start/end 位置算法。