从大型 json 文件 (~8GB) 中自动提取数据
Posted
技术标签:
【中文标题】从大型 json 文件 (~8GB) 中自动提取数据【英文标题】:Automating data extraction from large json file (~8GB) 【发布时间】:2022-01-23 17:05:00 【问题描述】:我有一个相当大的 JSON 文件 (~8GB),格式如下:
// some fixed fields
"data": [
// unimportant fields
"importantField": "some alphanumeric string"
,
// same format as previous
,
...
]
我想将importantField
的所有值提取到一个单独的文件中,并且我想自动完成。
我尝试使用这个grep
命令
grep -E -o '"importantField":"[[:alnum:]]+"' file.json
但由于内存使用量大而导致进程终止(在某些时候它使用了我 80% 以上的内存,并且 GUI 没有响应)。
然后我尝试先使用split
命令将输入分成2GB的文件
split --bytes 2000000000 file.json out_
然后使用上面相同的grep
命令,这一次它在大约 30 秒内完成每个块的速度相当快。
这种我必须先拆分输入的方法对我来说很好,但唯一的问题是自动检查split
命令是否正确拆分文件,即不在中间importantField
密钥对,因为这会导致丢失一些重要数据。
我想知道是否有更好/更优雅的方式来做到这一点。
我还感兴趣的是为什么grep
不能处理 8GB 文件,但它却像处理 2GB 文件的魅力一样。我用于匹配的正则表达式似乎并不邪恶。
我的假设是它首先尝试加载整行(它使用了我一半的 RAM),然后它使用更多内存进行内部计算,这导致系统开始使用 SWAP 内存,这反过来又导致性能非常慢在终止程序之前(10 分钟以上)。
针对此问题的一些重要信息:
data
数组中的对象格式总是相同
输入的 json 被缩小,它不包含空格或换行
输入文件是静态的
我显然有兴趣提取所有的重要数据
【问题讨论】:
grep
、split
这样的工具在这种情况下是错误的。考虑使用jq
。
让我反问你:为什么不使用面向问题的工具?会不会被认为已经足够详细了? jq
旨在以流方式处理(shell)脚本中的 JSON 数据。它至少允许指定您感兴趣的 JSON 路径节点。我唯一不确定的是如何使用jq
对过滤后的值进行过滤(我只是从来没有这样做的理由)。 grep
不关心上下文语法(顺便说一句,您是否将 JSON 缩小以使 grep 内存不足?),split
也不可能破坏中间的 JSON 值(无论如何都会浪费文件)。
好的,我现在看到jq
流出奇地(非常?)慢,正如这里提到的:***.com/questions/62825963/…。该问题的人建议使用其他即用型工具。我真的很惊讶jq
速度太慢了。嗯……我没什么可说的。通过在链接问题中使用其中一种工具或实施为您唯一目的服务的自定义工具,您可能会获得更多运气和性能改进。就像我一样,盲目的信任有时非常糟糕。 :P
@chepner 我确实相信jq
可以构建一个有效的过滤器来过滤给定的 JSON 输入。我刚刚生成了一个虚拟的 6.5 Gb JSON 文件,上面的 jq
命令仍然(!!)运行,甚至没有产生一行,而我在 Java 中实现了一个带有 Gson 库的备用 JSON 流提取器,后者在我的机器上提取与正则表达式匹配的importantField
属性值大约需要 35 秒(即使在 Java 世界中,Gson 解析器也不是很有效)。
我刚刚杀死了运行超过 15 分钟的 jq
实例,甚至没有产生一行。我的 not-C-C++-Rust-but-slow-Java-slow-Gson 实现花了不到一分钟的时间来消耗整个 JSON 文件并将过滤后的输出生成到 /dev/null。不知道jq
是如何实现流式传输的,但是是的,现在我看到我必须承认我对使用jq
的建议非常糟糕,我会考虑找到一个更快的工具。
【参考方案1】:
有点困惑:
到目前为止,OP 还没有提供任何尝试的代码,所以我不确定提到的grep
命令将如何terminate due to large memory usage
不明白关于streaming
的讨论(输入是静态文件吗?它是不断写入的,所以我们需要某种tail -f
解决方案吗?)
假设:
OP 想要提取 ALL 次出现的"importantField"
的值
所有双引号成对出现
"importantField"
条目的匹配值显示在输入的同一行
输入的格式“很好”,如示例中所示(例如,每个属性/值对出现在各自的行上)
我没有遗漏一些完全否定这个答案的细节......
提取所需字段的一些想法:
sed -En 's/.*"importantField"[^"]*"([^"]+)".*/\1/p' sample.json
grep '"importantField"' sample.json | cut -d'"' -f4
awk -F'"' '$2=="importantField" print $4' sample.json
所有这些都会生成:
some alphanumeric string
放弃关于属性/值对显示在单独行上的假设:
$ cat sample.2.json
// some fixed fields "data": [ // unimportant fields "importantField": "some alphanumeric string" , // same format as previous , "importantField": "one more string"... ]
$ awk -F'"' '/importantField/ for (i=2;i<=NF;i=i+2) if ($i == "importantField") print $(i+2)' sample.2.json
some alphanumeric string
one more string
添加使用嵌入式换行符提取值的功能:
$ cat sample.3.json
// some fixed fields
"data": [
// unimportant fields
"importantField":
"some
alphanumeric
string"
,
// same format as previous
,
...
]
$ awk -F'"' 'BEGINRS="^$"/importantField/ for (i=2;i<=NF;i=i+2) if ($i == "importantField") print $(i+2)' sample.3.json
some
alphanumeric
string
鉴于广泛的 cmets(对 OP 的问题),我猜这些可能是过于简化的答案......我错过了什么?
【讨论】:
以上是关于从大型 json 文件 (~8GB) 中自动提取数据的主要内容,如果未能解决你的问题,请参考以下文章
在 PowerShell 中将大型 blob 从 SQL Server 提取到文件需要很长时间
如何使用设计自动化 API 从上传的 AutoCAD 文件中提取元数据?