나는 거대한 json 파일(50만 줄)을 가지고 있습니다.
특정 문자열이 포함된 항목 집합을 삭제해야 합니다.
{
"bla1": {
"Part1": "Plop1",
"Part2": "Plop2",
"Part3": "BadFling1<stuff>",
"part4": "Plop4",
},
"bla2": {
"Part1": "Plop1",
"Part2": "Plop2",
"Part3": "<stuff>",
"part4": "Plop4",
},
// etc for many more entries
}
모든 항목에는 "Part3" 항목 앞에 "BadFling1"이 붙습니다.
"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 ...
}
또한 [{
직접 상위 블록만 사용하므로 더 많은 중첩 수준이 있으면 작동하지 않습니다.
답변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
원본 버전에서 해당 항목을 제거하세요 . 설명에서 언급했듯이 이 솔루션을 사용하려면 블록 크기가 일관되고 일치하는 줄이 각 블록 내에서 동일한 위치에 있어야 합니다(예제에서와 같이).
그렇지 않은 경우(레코드의 크기가 다르거나 레코드 요소의 위치를 신뢰할 수 없는 경우) 빠른 구문 분석 스크립트를 작성하기 위한 힌트로 삼겠습니다. JSON 파서가 내장된 Python 몇 줄만 사용하면 이러한 레코드를 쉽고 안전하게 삭제할 수 있습니다.
답변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
정력 사용:
:%s/BadFling1//g
"BadFling1"이 검색되면 모두 ""로 대체됩니다.