我有一个巨大的 json 文件(二分之一百万行)。
我需要删除一组包含特定字符串的条目。
{
"bla1": {
"Part1": "Plop1",
"Part2": "Plop2",
"Part3": "BadFling1<stuff>",
"part4": "Plop4",
},
"bla2": {
"Part1": "Plop1",
"Part2": "Plop2",
"Part3": "<stuff>",
"part4": "Plop4",
},
// etc for many more entries
}
所有条目都将“BadFling1”作为“Part3”条目的前缀。
我想知道如何自动删除包含“BadFling1”的所有条目的最佳方法。例如,从上面删除错误条目的结果是:
{
"bla2": {
"Part1": "Plop1",
"Part2": "Plop2",
"Part3": "<stuff>",
"part4": "Plop4",
},
// etc for many more entries
}
我的第一次尝试是有效的,但速度不够快(因为它有点手动)。
/BadFling1
qan3k5ddq
:map z n@a
现在按住“z”键。
我的 vim foo 不够强大,所以我不确定如何在 vim 中更好地自动化流程。任何帮助表示赞赏。
bash 中的替代方法(也欢迎其他命令行工具)。
答案1
试试这个vim
:
:g/BadFling/normal [{V]}d
该:global
命令在与模式匹配的所有行上运行命令(我用作BadFling
示例 - 如果需要,请调整它)。在这种情况下运行的命令是:normal
运行正常模式命令的命令。这样做的目的是利用在括号对之间移动的[{
和移动命令的功能。 is]}
vim
组合Vd
用于进行逐行删除。这不像 JSON 解析器那么强大,但可以假设每个"blah1"
部分都包含在自己的行集中,因此按行删除不会意外删除属于另一个块的任何内容。例如,如果您有类似的内容,则逐行删除方法将不起作用
... end of block you want to keep
}, "blah1" : {
block you want removed
}, "blah2" : {
start of block you want to keep ...
}
此外,[{
only 使用直接父块,因此如果您有更多级别的嵌套,它也将不起作用。
答案2
如果您的 版本足够新,您可以使用grep
和执行此操作:diff
diff
ire@localhost$ grep -B 3 -A 2 BadFling1 huge.json | diff --changed-group-format="%>" --unchanged-group-format="" - huge.json
{
"bla2": {
"Part1": "Plop1",
"Part2": "Plop2",
"Part3": "<stuff>",
"part4": "Plop4",
},
// etc for many more entries
}
grep
通过提取匹配项周围的行来删除不良记录。将diff
它们从原始版本中删除。正如评论中提到的,此解决方案要求块大小一致,并且匹配行位于每个块内的同一位置(如您的示例所示)。
如果情况并非如此(记录大小不同,或记录元素的位置不可靠),我会将其作为编写快速解析脚本的提示。您只需几行 Python 即可轻松安全地删除这些记录,Python 具有内置的 JSON 解析器。
答案3
awk 中的解决方案如下:
awk '/".*":\ {/ { open=line; skip_block=0 }
/"Part3":\ "BadFling1/ { skip_block=1 }
/},/ { if (skip_block) { line=open; next } }
{ lines[line++]=$0 }
END { for (i=0;i<=line;i++) { print lines[i] } }' yourfile > clean
这还没有经过很好的测试,但它应该可以帮助您入门。即使块的长度可变并且不关心不合格行位于块中的何处,它也将起作用。
解释:
第 1 行:如果该行与块的开头匹配,请记下数组中的位置,将该块标记为到目前为止良好
第2行:如果该行与不合格行匹配,则标记该块
第 3 行:匹配块的末尾。如果该块被标记,则将数组中的位置重置为该块开始的位置,并跳到下一行
第 4 行:将当前行添加到数组并增加行计数器
第 5 行:读取文件后,打印数组,仅包含“好”块
您可以在 bash 中实现相同的功能,但 awk 会快得多,在我看来,这就是 awk 的用途,而无需使用“更重”的语言。
答案4
使用vim:
:%s/BadFling1//g
将搜索所有出现的“BadFling1”并将其替换为“”。