入力ファイルです。
QSUM HEADER STOCK DATE TIME
206 CC-USER REJECT SENT TNPPP 200322 104914600
TI JPS TNN LTNN PP JP
8 6 0 1
AA NS CPOODE
4 899599991119
TYPE AI
12 18
QSUM HEADER STOCK DATE TIME
206 CC-USER REJECT SENT TNPPP 200322 115844000
TI JPS TNN LTNN PP JP
8 6 0 1
AA NS CPOODE
4 899599991555
TYPE AI
12 18
QSUM HEADER STOCK DATE TIME
103 SUITE FAIL, SUBTRACT FAILURE TPNRM 200318 031124100
TI PNC TNN PP JP AA
2 1499 177 123 1 4
NS FLAG
999999999999 ORIGIN
TI CPO
QSUM HEADER STOCK DATE TIME
103 SUITE FAIL, SUBTRACT FAILURE TPNRM 200318 031124200
TI PNC TNN PP JP AA
2 1499 177 123 1 4
NS FLAG
999999999999 ORIGIN
TI CPO
次の出力が必要です
QSUM HEADER STOCK DATE TIME TI PNC JPS TNN LTNN PP JP AA NS FLAG OPCODE TYPE AI TI CPO
206 CC-USER REJECT SENT TNPPP 200322 104914600 8 6 0 1 4 899599991119 12 18
206 CC-USER REJECT SENT TNPPP 200322 115844000 8 6 0 1 4 899599991555 12 18
103 SUITE FAIL, SUBTRACT FAILURE TPNRM 200318 031124100 2 1499 177 123 1 4 999999999999 ORIGIN
103 SUITE FAIL, SUBTRACT FAILURE TPNRM 200318 031124200 2 1499 177 123 1 4 999999999999 ORIGIN
私は次のサンプルawkライナーを試しました
awk 'BEGIN{print ("QSUM HEADER STOCK DATE TIME TI PNC JPS TNN LTNN PP JP AA NS FLAG OPCODE TYPE AI TI CPO")}
/^[0-9]/{print a["o"] " " a["p"] " " a["q"]
;delete a}
/^[0-9]/{a["o"]=$0
next}
/TNN/{getline
a["p"]=$0
next}
/NS/{getline
a["q"]=$0
next}
END
{print a["o"] " " a["p"] " " a["q"];}'
各ヘッダー型のパターンを一致させた後、次の行を配置する必要があります。この結果を達成するための他の良い選択肢。このコードは私にとってうまくフィットしますが、ここで問題は、ヘッダーと同じくらい長くなるということです。
答え1
FPAT
and にはGNU awkを使用します(次にFIELDWIDTHS
gawksの\s/\S
略語を使用します[[:space:]]/[^[:space:]]
):
$ cat tst.awk
BEGIN { OFS="\t" }
/^\s*$/ { next } # skip blank lines
/^\s*QSUM\s/ { # start of a new record
numRecs++
lineNr = 0
}
{
if ( (++lineNr) % 2 == 1 ) {
# tags line so find every tag and field width
FPAT = "\\S+\\s*"
$0 = $0
for (i=1; i<=NF; i++) {
tag = $i
fw = (i>1 ? fw " " : "") (i<NF ? length(tag) : "*")
gsub(/^\s+|\s+$/,"",tag)
if ( !(tag in tagNames2nrs) ) {
tagNrs2names[++numTags] = tag
tagNames2nrs[tag] = numTags
}
fldNr2tagNr[i] = tagNames2nrs[tag]
}
FPAT = ""
}
else {
# vals line so use the field widths found for tags
FIELDWIDTHS = fw
$0 = $0
for (i=1; i<=NF; i++) {
val = $i
gsub(/^\s+|\s+$/,"",val)
tagNr = fldNr2tagNr[i]
vals[numRecs,tagNr] = val
}
FIELDWIDTHS = ""
}
}
END {
for (tagNr=1; tagNr<=numTags; tagNr++) {
tag = tagNrs2names[tagNr]
printf "%s%s", tag, (tagNr<numTags ? OFS : ORS)
}
for (recNr=1; recNr<=numRecs; recNr++) {
for (tagNr=1; tagNr<=numTags; tagNr++) {
val = vals[recNr,tagNr]
printf "%s%s", val, (tagNr<numTags ? OFS : ORS)
}
}
}
。
$ awk -f tst.awk file | column -s$'\t' -t
QSUM HEADER STOCK DATE TIME TI JPS TNN LTNN PP JP AA NS CPOODE TYPE AI PNC FLAG CPO
206 CC-USER REJECT SENT TNPPP 200322 104914600 8 6 0 1 4 899599991119 12 18
206 CC-USER REJECT SENT TNPPP 200322 115844000 8 6 0 1 4 899599991555 12 18
103 SUITE FAIL, SUBTRACT FAILURE TPNRM 200318 031124100 2 177 123 1 4 999999999999 1499 ORIGIN
103 SUITE FAIL, SUBTRACT FAILURE TPNRM 200318 031124200 2 177 123 1 4 999999999999 1499 ORIGIN
各行ペアには異なるフィールドがあり、その一部は空であるため、データを読み取るには固定フィールドに依存する必要がありますが、読み取ろうとする前にフィールド数とフィールド幅を知ることはできません。したがって、このスクリプトはFPATを使用して、各タグ行内のすべてのタグ(名前、タイトル、タイトル)を検索します。
TI JPS TNN LTNN PP JP
各フィールドの幅(タグ名と次のスペースを含む)を決定し、FIELDWIDTHSを使用して後続の値行から値を読み取ることができます。
8 6 0 1
固定幅のフィールドなので、フィールドが空の場合も同様です。
私はそれについて言及したり、一般的に説明しません。 IMHOコードは非常に明確で、テキスト操作に関する多くの質問をしていたので、いくつかのコードを読んでawkについて学ぶ時間です。マニュアルページを参照してくださいprint
。変数がどの値を持っているかを確認する必要がある場所にステートメントなどを追加する必要があります。具体的な質問がある場合は、お気軽にお問い合わせください。
上記はあなたの質問と同じファイルであるこの入力ファイルに対して実行されましたが、QSUM、STOCK、DATA、およびTIMEヘッダーの一貫性のないソートが修正されました。あなたの実際の入力がそれほど混乱しているとは思わないからです。 up(しかし、そうであれば処理しやすいでしょう):
$ cat file
QSUM HEADER STOCK DATE TIME
206 CC-USER REJECT SENT TNPPP 200322 104914600
TI JPS TNN LTNN PP JP
8 6 0 1
AA NS CPOODE
4 899599991119
TYPE AI
12 18
QSUM HEADER STOCK DATE TIME
206 CC-USER REJECT SENT TNPPP 200322 115844000
TI JPS TNN LTNN PP JP
8 6 0 1
AA NS CPOODE
4 899599991555
TYPE AI
12 18
QSUM HEADER STOCK DATE TIME
103 SUITE FAIL, SUBTRACT FAILURE TPNRM 200318 031124100
TI PNC TNN PP JP AA
2 1499 177 123 1 4
NS FLAG
999999999999 ORIGIN
TI CPO
QSUM HEADER STOCK DATE TIME
103 SUITE FAIL, SUBTRACT FAILURE TPNRM 200318 031124200
TI PNC TNN PP JP AA
2 1499 177 123 1 4
NS FLAG
999999999999 ORIGIN
TI CPO
答え2
私はこの問題を一度に解決したいと思います。 BEGINセクションから始めて、目的の配列列を含む配列を作成します。出力、のための。列の幅とテキスト(最初):
BEGIN {
defCol[ 1] = " 5 QSUM";
defCol[ 2] = " 28 HEADER";
...
defCol[19] = " 6 CPO";
}
タイトルのみを印刷する関数を作成してこれを確認してください。次のようにdefColを繰り返すことができます。
printf ("%.*s", 0 + defCol[j], substr (defCol[j], 5);
その後、空行を省略し、1 つの QSUM 入力と次の QSUM 入力の間の入力行セットを収集する関数を作成します。大文字と空白のみを含む行はヘッダー、他のコンテンツを含む行はデータであり、フィールドは前のヘッダー行に合わせてソートされます。 (入力と出力の一番上の行が誤ってソートされているのが誤字であると仮定します。)
各グループについて、ヘッダーを列名と一致させて、データ項目が属するハトの集まりを決定します(当然、defColと同じ配列へのインデックスとして)。その後、defColインデックスを使用してフィールドを並べ替え、printfフォーマッタのdefCol幅を使用して詳細行を印刷できます。
複雑に見えますが、柔軟で(近い将来、他の同様の問題に直面するようです)、体系的です。以前に見たことのないヘッダー、レポートに長すぎるフィールドなどを表示することもできます。
より詳細な機能説明:
まず、入力グループが完了するタイミングを検出する条件が必要です。これは「制御中断」でした。これは「QSUM HEADER」または同様の内容を含む行で発生し、いくつかのパターンを使用して異なる間隔を使用できます。また、(まだグループがないため)このタスクが最初に発生したくないため、END条件でも発生する必要があります(最後のグループにはトリガーするタイトルがないため)。 )。
入力の残りの部分を使用して、すべてのヘッダー部分を1つの長い文字列に追加し、すべてのデータ部分を別の長い文字列に追加し、すべての空行を無視できます。
グループを出力するには、ヘッダー文字列を介してデータヘッダーの位置を見つけ、match()およびsubstr()関数を使用してデータ文字列内のその場所のフィールドを選択します。ヘッダ項目と同じインデックスを使用して、各データ項目を配列に格納できます。
その後、タイトル固定テキストを出力するのと同じように、データフィールドを出力できます。
これらすべてが混乱して聞こえますが、実際には非常に簡単です。今はコーディングできませんが、今日後でフレームワークを公開できます。
実際、入力(公開)は、間隔の変化のために安定して解析されない可能性があります。
たとえば、STOCK、DATE、TIMEの下の値が一致しません。 HEADERにはスペースがあるため、フィールド数を数えることはできません。複数のスペースを区切り文字として使用するか、「十分に近い」フィールドソートを使用します。これは40行の例です。他のリスクもあります。