そのジャンルの 1 番目の記事、そのジャンルの 2 番目の記事、...のように記事を保存しておく必要があります。 そして記事全体を読み込んだ後で、 それらを各ジャンル毎に出力していくことになります。
つまり、ジャンルが複数存在して、 その各ジャンルに記事が複数存在することになりますが、 そのような記事の保存のために 2 次元配列を利用することにします。
2 次元配列とは、 配列の添え字を 2 次元的に添え字付けられる配列のことを言います。 AWK の配列は連想配列で、元々添え字に文字列も使用できますから、 それを工夫すれば、例えば
a["1x1"], a["1x2"], a["2x1"], a["2x2"], ...のようにして容易に 2 次元配列を作れるのですが、 AWK 自体に 2 次元配列という仕組みが用意されています。
C 言語では、2 次元配列は a[i][j] のように表現しますが、 AWK の 2 次元配列は a[i, j] のように添え字を `,' で区切って 指定することになっています1。
よって、例えば、
a[1,1]=1; a[1,2]=2; a[2,1]="x"; a[2,2]="y";のような使い方ができますし、文字列を添え字にして、
a["abc",3]=3のように使うこともできます。
今回は「 番目のジャンルの 番目の記事」を hs[i,j] という 2 次元配列に保存することにします。
これを利用すれば、全体の AWK の疑似コードは以下のように書けます。
BEGIN{ # NG = 全ジャンル数 # hn[j] = j 番目のジャンルの記事数 (1<=j<=NG+1) # ((NG+1) 番目のジャンルは「その他」の記事) # hs[j,k] = j 番目のジャンルの記事を保存する配列 (1<=k<=hn[j]) # # (1) j 番目のジャンルのキーワードパターン pat[j] を定義 (1<=j<=NG) } ($0 ~ /^<h2>/){ title=$0 } ($0 ~ /^<li>/){ # (2) <li> 行から見出し部分だけを取り出す (= str とする) for(j=1;j<=NG;j++){ if(str ~ pat[j]){ hn[j]++ hs[j, hn[j]]=$0 break } } if(j>=NG+1){ # いずれにもマッチしなかった場合。この場合 j==NG+1 hn[j]++ hs[j, hn[j]]=$0 # 「その他」に保存 } } END{ # (3) HTML ヘッダ等の出力 for(j=1;j<=NG+1;j++){ # (4) hs[j,1] ~ hs[j,hn[j]] を出力 } # (5) HTML フッタ等を出力 }
($0 ~ /^<h2>/)
は <h2> で始まる行に対する処理を意味しますが、
これは /^<h2>/
とだけ書いても構いません。
この見出しの行は title という変数に保存していて、
最後のヘッダの出力で利用します。
($0 ~ /^<li>/)
のブロックが 各 <li> 行に対する処理ですが、この中で各ジャンルにマッチするかをパターンマッチングを利用して調べています。
正規表現とのパターンマッチングは、通常は
str ~ /正規表現/のように書くのですが、正規表現をあらかじめ変数 (例えば ) に保存しておいて、
str ~ patのように書いても同じことになります2。 この場合、変数 に代入する文字列には / / は含めませんし、
str ~ /pat/
とも書きません。
逆に str ~ /pat/
と書いてしまうと ``pat'' という文字列とのマッチングを意味してしまいます。
パターンにマッチした場合には、 そのジャンルに追加するために記事数を一つ増やし (hn[j]++)、 記事をそのジャンルの配列の最後に追加しています (hs[j,hn[j]]=str)。 その場合はそれ以降のマッチングをやる必要がないので break で for 文を抜けだしています。
いずれにもマッチしなかった場合は、for 文のインデックスの が になっているはずですから、 それを判別してその 番目のジャンル (「その他」) に保存しています。
なお、for 文の break を next に置きかえれば、 いずれかにマッチすれば for 文の次の文に進むことはなくなりますので、 この for 文の次の if によるチェックは必要ありません。 今回も最終的にはそのように書くことにします。
(3) や (5) のヘッダやフッタの出力は、 [4] や [5] と同様の関数を使って簡単に出力できます。 (2) の部分も、[4] や [5] で述べたように、 sub() などを使って容易に取り出すことができます。 (4) の部分もほぼそのまま並べるだけなので、 よって考えるべき部分は (1) のみ、となります。