다음과 같은 파일이 있다고 가정해 보겠습니다.
A random Title 1
BLOCK
1- a block of text that can contain any character
and it also can contain multiple lines
BLOCK
A random Title 2
BLOCK
2- a block of text that can contain any character
and it also can contain multiple lines
BLOCK
A random Title 3
BLOCK
3- a block of text that can contain any character
and it also can contain multiple lines
BLOCK
파일에는 이와 같은 텍스트 블록이 여러 개 있을 수 있습니다. 다음 JSON을 통해 이 텍스트의 매개변수를 배포하고 싶습니다.
[
{
"title": "A random Title 1",
"body": "1- a block of text that can contain any character\nand it also can contain multiple lines"
},
{
"title": "A random Title 2",
"body": "2- a block of text that can contain any character\nand it also can contain multiple lines"
},
{
"title": "A random Title 3",
"body": "3- a block of text that can contain any character\nand it also can contain multiple lines"
}
]
파일 문자를 한 문자씩 통과하는 루프를 생성하여 이 문제를 해결할 수 있다는 것을 알고 있으며 그런 다음 JSON의 모든 변수를 올바르게 나누는 논리를 생성할 수 있습니다. 하지만 명령줄을 사용하는 더 쉬운 솔루션이 있는지 궁금합니다. AWK를 사용하여 파일에서 얻은 매개변수를 JSON 출력에 배포할 수 있나요? 아니면 이 경우 AWK가 무엇을 하는지 제가 오해하고 있는 걸까요?
답변1
당신은 시도 할 수 있습니다밀러
$ mlr --inidx --irs '\n\n' --ifs 'BLOCK' --ojson --jvstack --jlistwrap \
put -S 'for(k,v in $*){$[k] = strip(v)}' then \
cut -f 1,2 then \
rename '1,title,2,body' file
[
{
"title": "A random Title 1",
"body": "1- a block of text that can contain any character\nand it also can contain multiple lines"
}
,{
"title": "A random Title 2",
"body": "2- a block of text that can contain any character\nand it also can contain multiple lines"
}
,{
"title": "A random Title 3",
"body": "3- a block of text that can contain any character\nand it also can contain multiple lines"
}
]
파이프를 사용하여 출력을 예쁘게 만들 jq '.'
거나 --jvstack --jlistwrap
옵션을 생략하고 파이프 할 수 있습니다 jq -s '.'
.
$ mlr --inidx --irs '\n\n' --ifs 'BLOCK' --ojson \
put -S 'for(k,v in $*){$[k] = strip(v)}' then \
cut -f 1,2 then
rename '1,title,2,body' file | jq -s '.'
[
{
"title": "A random Title 1",
"body": "1- a block of text that can contain any character\nand it also can contain multiple lines"
},
{
"title": "A random Title 2",
"body": "2- a block of text that can contain any character\nand it also can contain multiple lines"
},
{
"title": "A random Title 3",
"body": "3- a block of text that can contain any character\nand it also can contain multiple lines"
}
]
이는 cut -f 1,2
두 번째 태그가 세 번째(빈) 필드를 의미하기 때문에 필요합니다 BLOCK
. 원하는 경우 동사로 바꿀 수 있습니다 remove-empty-columns
(후자는 스트리밍이 아니지만).
본문의 연속 개행 처리
불행하게도 위의 내용은 입력 레코드 구분 기호인 연속 개행과 BLOCK
... BLOCK
본문 구분 기호 사이에 나타날 수 있는 연속 개행을 구분하지 않습니다. 해결 방법으로 입력을 사전 처리하여 \n
본문의 줄 바꿈을 순차적으로 바꾼 다음 JSON을 작성하기 전에 이를 리터럴 줄 바꿈으로 다시 바꿀 수 있습니다( \n
여기서 Miller는 이를 다시 이스케이프합니다).
sed '/^BLOCK/{:a;N;/BLOCK$/!ba;s/\n/\\n/g;}' file |
mlr --inidx --irs '\n\n' --ifs 'BLOCK' --ojson put -S '$2 = gsub($2,"\\n","\n"); for(k,v in $*){$[k] = strip(v)}' then cut -f 1,2 then rename '1,title,2,body'
sed 필터를 Miller에 명령으로 전달할 수 있지만 --prepipe
인용이 까다로워집니다.
답변2
내부에TxR언어를 사용하면 다음과 같이 할 수 있습니다.
$ txr data.txr data
[{"title":"A random Title 1","body":"1- a block of text that can contain any character\nand it also can contain multiple lines"},
{"title":"A random Title 2","body":"2- a block of text that can contain any character\nand it also can contain multiple lines"},
{"title":"A random Title 3","body":"3- a block of text that can contain any character\nand it also can contain multiple lines"}]
코드 data.txr
는 다음과 같습니다
@(bind vec @(vec))
@(repeat)
@title
BLOCK
@(collect)
@lines
@(until)
BLOCK
@(end)
@(cat lines "\n")
@(do (vec-push vec #J^{"title" : ~title, "body" : ~lines}))
@(end)
@(do (put-jsonl vec))
우리는 필수 JSON에 해당하는 기본 데이터 구조인 해시 벡터를 구축합니다.
접두사는 #J
Lisp에 포함된 JSON 리터럴을 나타냅니다. 여기에는 ^
텍스트가 준따옴표로 묶여 있음을 나타내는 문자 가 있습니다 ~
. 템플릿에 값을 삽입하는 따옴표가 없는 문자는 문자열과 개행 문자를 연결하는 수집된 줄을 기반으로 본문을 계산하는 표현식입니다.
put-jsonl
즉 put-json
, 그 뒤에 개행 문자가 있습니다. 기본적으로 *stdout*
스트림에 있습니다.
다음과 같이 들여쓰기를 사용하는 것이 좋습니다.
@(bind vec @(vec))
@(repeat)
@ title
BLOCK
@ (collect)
@ lines
@ (until)
BLOCK
@ (end)
@ (cat lines "\n")
@ (do (vec-push vec #J^{"title" : ~title, "body" : ~lines}))
@(end)
@(do (put-jsonl vec))
이는 일종의 awk를 사용하여 수행할 수 있습니다. TXR Lisp의 Awk 매크로:
$ txr data.tl data
[{"title":"A random Title 1","body":"1- a block of text that can contain any character\nand it also can contain multiple lines"},
{"title":"A random Title 2","body":"2- a block of text that can contain any character\nand it also can contain multiple lines"},
{"title":"A random Title 3","body":"3- a block of text that can contain any character\nand it also can contain multiple lines"}]
암호:
(awk
(:set rs "\n\n" fs "\n")
(:let (vec (vec)))
((and (equal [f 1] "BLOCK")
(equal [f -1] "BLOCK"))
(vec-push vec #J^{"title":~[f 0], "body":~(cat-str [f 2..-1])})
(next))
(t (error "bad data"))
(:end (put-jsonl vec)))
이 블록은 초기화에 사용되며 레코드 구분 기호 및 필드 구분 기호를 (:set ...)
설정하는 데 사용되며 원본 Awk 및 . 개행 필드 구분 기호와 이중 개행 레코드 구분 기호를 사용하여 각 정보 덩어리를 다음과 같이 필드가 있는 레코드로 처리합니다.rs
fs
RS
FS
"title" "BLOCK" "body1" "body2" ... "bodyn" "BLOCK"
awk 매크로에서 필드는 명명된 list 로 제공됩니다 f
.
주요 논리는 (조건부 동작) 쌍입니다. 요구 사항은 다음과 같습니다
(and (equal [f 1] "BLOCK") (equal [f -1] "BLOCK"))
f
의 두 번째 요소와 마지막 요소가 문자열이면 참입니다 "BLOCK"
. 이것이 사실이라면 첫 번째 프로그램에서와 마찬가지로 조각을 추출하고 vec
JSON 준따옴표를 사용하여 항목을 추가하는 이 작업을 수행합니다. 또한 (next)
다음 조건-작업 쌍이 발생하지 않도록 다음 레코드로 이동을 수행합니다.
다음 조건-작업 쌍은 true (t (error ...))
이므로 항상 실행되고 t
예외가 발생합니다.
우리는 전통적인 Awk (:end ..)
처럼 JSON을 덩어리로 인쇄합니다 .END { ... }
오류 검사에 관해 말하자면, 첫 번째 프로그램은 잘못된 데이터를 어느 정도 허용합니다. 잘못된 입력을 거부하도록 미세 조정하는 방법이 있습니다. 예를 들어 자동으로 건너뛰는 레코드 사이에 가비지가 있을 수 있으며 마지막 끝 블록이 누락되어도 괜찮습니다.
답변3
모든 Unix 시스템의 모든 쉘에서 awk를 사용하십시오.
$ cat tst.awk
BEGIN {
RS = ""
FS = "\n"
printf "["
}
{
gsub(/"/,"\\\\&")
title = $1
body = $3
for (i=4; i<NF; i++) {
body = body "\\n" $i
}
print (NR>1 ? "," : "")
print " {"
printf " \"title\": \"%s\",\n", title
printf " \"body\": \"%s\"\n", body
printf " }"
}
END {
print "\n]"
}
$ awk -f tst.awk file
[
{
"title": "A random Title 1",
"body": "1- a block of text that can contain any character\nand it also can contain multiple lines"
},
{
"title": "A random Title 2",
"body": "2- a block of text that can contain any character\nand it also can contain multiple lines"
},
{
"title": "A random Title 3",
"body": "3- a block of text that can contain any character\nand it also can contain multiple lines"
}
]
답변4
Python은 itertools
모듈과 함께 작동하여 입력을 청크/문단으로 그룹화한 다음 json
덤프 메소드가 있는 모듈을 사용하여 json 스타일로 인쇄합니다.
python3 -c 'import sys, json, itertools
ifile,rs = sys.argv[1],chr(10)
lod = []
with open(ifile) as fh:
for k,g in itertools.groupby(fh, lambda x: x == rs):
if not k:
para = list(g)
title,x,*body = list(map(lambda x: x.rstrip(rs),para[0:-1]))
lod.append({
"title": title,
"body": rs.join(body)
})
print(json.dumps(lod, sort_keys=False, indent=4))
' file
산출:
[
{
"title": "A random Title 1",
"body": "1- a block of text that can contain any character\nand it also can contain multiple lines"
},
{
"title": "A random Title 2",
"body": "2- a block of text that can contain any character\nand it also can contain multiple lines"
},
{
"title": "A random Title 3",
"body": "3- a block of text that can contain any character\nand it also can contain multiple lines"
}
]