次のようなファイルがあるとしましょう。
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"
}
]
ファイル文字を1文字ずつ通過するループを作成することでこの問題を解決できることがわかり、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
、2番目のタグが3番目の(空の)フィールドを意味するために必要です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マクロでは、フィールドは名前付きリストとして提供されますf
。
主なロジックは(条件付きアクション)ペアです。要件は次のとおりです。
(and (equal [f 1] "BLOCK") (equal [f -1] "BLOCK"))
f
の2番目と最後の要素が文字列の場合はtrueです"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"
}
]