次は、一覧部分の取得ですが、実はこちらは少し厄介です。
2 節で一覧の構造を少し紹介しましたが、 実際には少し違っていて、実は、各 <li> の行は別々の行にはなっておらず
<ul> <li>...<br><li>...<br><li>...<br><li>...<br><li>...<br><br> <li>...<br><li>...<br><li>...<br><li>...<br><li>...<br><br> <li>...<br><li>...<br><li>...<br><li>...<br><li>...<br><br> ..... </ul>(...の部分がリンクのためのタグや見出し、日時などの部分)
のように、いくつかの行がつながって長い行を作っていて、 いくつかのまとまり (5 個程度) 毎に最後の <br> が 2 つつながっていて そこでやっと改行が入っています。 よって 1 行を取得しても、そこに複数の <li> 行が含まれるので、 それを分割する必要があります。
<ul> や </ul> は、 上では単独行で書きましたが必ずしもそうであるとは限らず、 それも他の行につながっている場合もあるようです (実際最終ページの </ul> は直前の行の最後につながっている場合があります)。
このブロック部分の判別は、 HTML ファイル内に <ul></ul> ブロックがここにしか現れないようなので、 簡単のため <ul> にマッチするかどうかで判別することにし、 </ul> が現われるまで getline で取得することにします。
なお、明示的な getline を使わず、 フラグを使うことで現在 <ul></ul> 内であるかどうかを判別する、 という方法もありますが、それについては [4] を参照してください。
AWK の疑似コードは以下のようになります。
##### 各行の取得 ##### ($0 ~ /<ul>/){ sub(/<ul>/,"") if($0 !~ /<li>/) getline do{ # (1) 1 行を複数の <li> 行に分割して配列に保存 # (2) それを整形して出力 getline }while($0 !~ /<\/ul>/) if($0 ~ /<li>/){ sub(/<\/ul>/,"") # (1) 1 行を複数の <li> 行に分割して配列に保存 # (2) それを整形して出力 } }
ところで、これだと同じ処理 (上の (1),(2)) を 2 箇所に書くことになりますが、 それを 1 箇所にまとめるには、ループの中の getline を最初に持ってきて、
($0 ~ /<ul>){ sub(/<ul>/,"") do{ getline # (1) 1 行を複数の <li> 行に分割して配列に保存 # (2) それを整形して出力 }while($0 !~ /<\/ul>/) }とでもすれば済みそうですが、 これだと最初の行が <ul> のすぐ後ろから <li> が始まっている場合はそれを捨てることになってしまいますし、 最後が </ul> 単独の行である場合にも対応しておらず その行に対して (1),(2) を実行してしまいます。
(1),(2) の部分をループの中に 1 回だけしか書かないようにするには、 フラグなどを用いて次のようにする方法があります:
##### 各行の取得 その 2 ##### ($0 ~ /<ul>){ sub(/<ul>/,"") flag=0 do{ if(flag==1 || $0 !~ /<li>/) getline flag=1 if($0 ~ /<\/ul>/ && $0 !~ /<li>/) break # (1) 1 行を複数の <li> 行に分割して配列に保存 # (2) それを整形して出力 }while($0 !~ /<\/ul>/) }しかしこれだと、最初の if 文が意味があるのは <ul></ul> ブロックの最初の 1 行だけで、 その他の行に関しては if 文の判断は無駄になりますし、 2 つ目の if 文も最後の行にしか意味がないので、 その他の行に関しては if 文の判断は無駄です。
最初のコードと最後のコードは、(1),(2) のような部分を何度も書いていない、 という点では後者の方が綺麗に見えるかもしれませんが、 ループの中で無駄な処理を何度も繰り返すことになる、 という点では前者の方が無駄はなくてよいコード、ということになります。
どちらを採用するかは、ある程度は趣味の問題ですが、 とりあえずは最初のコードの方を採用することにします。
なお (2) の部分は、この時点で出力を行なうかわりに 全体を一つの配列に保存しておいて、END ブロックでまとめて出力する、 という方法もあります。 このようにすると、全体の項目数を知ることもできますし、 全体を逆順に出力することも可能になりますので、 今回もそのような方向で設計することにします。