使用 vim 从 json 中删除大块

使用 vim 从 json 中删除大块

我有一个巨大的 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和执行此操作:diffdiff

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”并将其替换为“”。

相关内容