json 如何用jq读取100+GB的文件而不耗尽内存

acruukt9  于 2022-11-19  发布在  其他
关注(0)|答案(3)|浏览(237)

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

bxgwgixi

bxgwgixi1#

jq的流解析器(使用--stream选项调用)通常可以处理非常非常大的文件(如果满足某些条件,甚至可以处理任意大的文件),但是它通常非常慢,而且通常非常麻烦。
在实践中,我发现像jstream和/或我自己的jm这样的工具在处理ginormous文件时与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)'
o0lyfsai

o0lyfsai2#

确定包含单个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("/")
v1uwarro

v1uwarro3#

我在这里发布了一个相关的问题: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"
]

如果您有一个数GB大或有数百万个键的单个顶级对象,则这种方法 * 不 * 起作用。

相关问题