본문의 연속 개행 처리

본문의 연속 개행 처리

다음과 같은 파일이 있다고 가정해 보겠습니다.

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에 해당하는 기본 데이터 구조인 해시 벡터를 구축합니다.

접두사는 #JLisp에 포함된 JSON 리터럴을 나타냅니다. 여기에는 ^텍스트가 준따옴표로 묶여 있음을 나타내는 문자 가 있습니다 ~. 템플릿에 값을 삽입하는 따옴표가 없는 문자는 문자열과 개행 문자를 연결하는 수집된 줄을 기반으로 본문을 계산하는 표현식입니다.

put-jsonlput-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 및 . 개행 필드 구분 기호와 이중 개행 레코드 구분 기호를 사용하여 각 정보 덩어리를 다음과 같이 필드가 있는 레코드로 처리합니다.rsfsRSFS

"title" "BLOCK" "body1" "body2" ... "bodyn" "BLOCK"

awk 매크로에서 필드는 명명된 list 로 제공됩니다 f.

주요 논리는 (조건부 동작) 쌍입니다. 요구 사항은 다음과 같습니다

(and (equal [f 1] "BLOCK") (equal [f -1] "BLOCK"))

f의 두 번째 요소와 마지막 요소가 문자열이면 참입니다 "BLOCK". 이것이 사실이라면 첫 번째 프로그램에서와 마찬가지로 조각을 추출하고 vecJSON 준따옴표를 사용하여 항목을 추가하는 이 작업을 수행합니다. 또한 (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"
    }
]

관련 정보