json (已解决)如何使用jq读取100+GB的文件而不耗尽内存

whitzsjs  于 2023-03-09  发布在  其他
关注(0)|答案(3)|浏览(193)

我有一个100+GB的json文件,当我试图用jq读取它时,我的计算机一直在运行我们的ram。有没有一种方法可以在限制内存使用的同时读取文件,或者其他方法可以读取一个非常大的json文件?
我在命令中键入的内容:jq 'keys' fileName.json

jaql4c8m

jaql4c8m1#

jq的流式解析器(使用--stream选项调用)通常可以处理非常非常大的文件(甚至在满足某些条件的情况下处理任意大的文件),但是它通常非常慢并且通常相当麻烦。
在实践中,我发现像jstream和/或我自己的jm这样的工具与jq结合在一起处理巨大的文件时非常好用,当这样使用时,它们都非常容易使用,尽管安装可能有点麻烦。
不幸的是,如果您对JSON文件的内容一无所知,只知道jq empty耗时太长或失败,那么据我所知,没有CLI工具可以自动生成有用的模式。然而,查看文件的前几个字节通常会提供足够的信息。或者,您可以从jm count开始,给予顶层对象的计数。如果顶级键是JSON对象,jm -s | jq 'keys[]'将给予顶级键的列表。
下面是一个例子,假设我们已经确定ginormous.json文件的大小主要是因为它包含了一个很长的顶级数组,然后假设schema.jq(本页其他地方已经提到过)在pwd中,那么您有希望通过运行以下命令找到一个信息丰富的模式:

jm ginormous.json |
  jq -n 'include "schema" {source:"."}; schema(inputs)'

另请参见jq to recursively profile JSON object,了解更简单的模式推理引擎。

cnh2zyt3

cnh2zyt32#

确定包含单个JSON实体的超大文件的结构的一种通用方法是运行以下查询:

jq -nc --stream -f structural-paths.jq huge.json | sort -u

其中structural_paths.jq包含:

inputs
| select(length == 2)
| .[0]
| map( if type == "number" then 0 else . end )

注意,输出中的“0”表示在相应的位置至少有一个有效的数组索引,而不是“0”实际上是该位置的有效索引。
还要注意,对于非常大的文件,使用jq --stream处理整个文件可能会非常慢。

示例:

给定{"a": {"b": [0,1, {"c":2}]}},上述咒语的结果将是:

["a","b",0,"c"]
["a","b",0]

顶层结构

如果你只是想了解更多关于顶层结构的信息,你可以简化上面的jq程序:

inputs | select(length==1)[0][0] | if type == "number" then 0 else . end

指定深度的结构

如果命令行sort失败,那么您可能希望限制路径的数量,只考虑特定深度的路径。
如果深度不是太大,那么希望您的命令行sort能够管理;如果没有,那么使用命令行uniq至少会稍微调整输出。
更好的选择可能是在jq中定义unique(stream),然后使用它,如下所示:

# Output: a stream of the distinct `tostring` values of the items in the stream
def uniques(stream):
  foreach (stream|tostring) as $s ({};
     if .[$s] then .emit = false else .emit = true | .item = $s | .[$s]=true end;
     if .emit then .item else empty end );

def spaths($depth):
  inputs
  | select(length==1)[0][0:$depth]
  | map(if type == "number" then 0 else . end);

uniques(spaths($depth))

jq的一个合适的调用如下所示:

jq -nr --argjson depth 3 --stream -f structural-paths.jq huge.json

除了避免排序的开销之外,使用uniques/1还将保留原始JSON中路径的顺序。

“JSON指针”指针

如果你想把数组路径表达式转换成“JSON指针”字符串(例如,用于jmjstream),只需在相关的jq程序中添加以下代码:

| "/" + join("/")
krcsximq

krcsximq3#

我在这里发布了一个相关的问题:Difference between slurp, null input, and inputs filter
如果您的文件很大,但文件中的文档并不是很大(只是许多小文档),jq -n 'inputs'可以帮助您开始:

jq -n 'inputs | keys'

下面是一个示例(包含一个小文件):

$ jq -n 'inputs | keys' <<JSON
{"foo": 21, "bar": "less interesting data"}
{"foo": 42, "bar": "more interesting data"}
JSON
[
  "bar",
  "foo"
]
[
  "bar",
  "foo"
]

如果您只有一个数千兆字节大小或有数百万个键的顶级对象,那么这种方法就不起作用。

相关问题