Perl覚え書き20041205

 前回「ブラウザはその属性値に > が出てこないことを知っているから、たとえば font size の値で " の対応が合っていなくても途中に > が出てきた時点でそのタグが終わりであると判断できる」という主旨の事を書いたが、どうやらそういうわけでもなかったようだ。

 テスト用として使っていたある時点でのはてなのHTMLソース、前回の最後の引用ブロックの内容だけど、ここがそうなっているにも関わらず画面にはちゃんと "(2)" と書かれいるように思っていた。が、改めてブラウザで表示してみるとそんなことはなくて開き括弧が size に食われて "2)" という表示になっていた。

 つまり、ブラウザは属性値として > が出てくる可能性があるのかどうかを知らない、もしくはチェックしていないということになる。dtd をいくつか見てみたが、CDATA としか書かれてなかった。キャラクターデータって事だよねえ?見方がよく分からないからもしかしたらどこかに書いてあったのかもしれないけど…。

 結局 allow_tags の値として特別な数値を持たせる必要はなく、これまでの解釈で問題ない。

 というわけでこれまでのものを convTag.pm に実装してみた。これまでと同様、基本的には convTagsAll 関数を使えば問題ないが、convTag 関数も export するようにした。これは convTag が引数でタグを与えて処理済みのを返すだけに戻したから。ただしこれは単一のタグしか受け付けず、特に内容に関してチェックはしていないため使用に関しては注意が必要。

 さて、今回はけっこう変わっている。convTagsAll は convTag の戻り値の変更による調整ぐらいだが、convTags はもうごっそり変わっている。前に書いた getToken なんてもう忘却の彼方。影も形もないなあ…。

 それから参照渡し、参照返しについても必要のない部分は元に戻した。参照で渡しても開始早々デリファレンスして変数に代入していたのでは意味がないから。でも convTagsAll はなんとか参照渡しの方法でやった方がいいのかもしれないなあ。けっこう大きなデータが渡される可能性があるし…。使い慣れていないからとりあえずこの方式で行って、最後にその方式に変更した方がいいんじゃないかと。どっちが原因でうまく動作しないのかが分からなくなると困るから。

 あとは allow_tags の定義とタグにマッチする正規表現の宣言部分を別ファイルに分けたこと。allow_tags については当然その方がわかりやすいから。正規表現を分けたのは、正規表現を直すとき、うまく動作するかを別のスクリプトでチェックするんだけど、それぞれ別にコピーペーストしてあると、どれが直してどれが元のままなのかが分からなくなってしまうためにここだけ書き換えれば全てが変わるようにした方がいいと考えたため。最終的に修正する必要がなくなったら pm ファイルに直接書いても問題ないし、そうした方がいいだろう。

 あ、別ファイルにすると allow_tags が見えないから pm に直接書いていると書いたことがあったが、それは単に my で宣言していたからだった。my を外したら見えるようになった。require を書いた部分がそのまま require されたファイルの内容に置き換えられるだけかと思っていたんだけどそういうわけじゃなかったのね。うーん。

 動作の流れとしては今まで通り convTagsAll でタグ部分を抜き出して convTag に渡し、タグより前の部分と、必要に応じて変換されたタグを連結していくだけだ。ここでの正規表現は自分で修正したものを使っている。やっぱりたぶんこっちを使った方がいいんじゃないかと。まあ速度的に不利な部分もあるかもしれないが(チェックはしてない)。まあそれに convTag の方ではこちらの正規表現が必要になってくる。

 タグのマッチ正規表現は二種類を使っていて、どちらもほぼ同じ。どこが違うのかというと、convTagsAll で使っている方はコメントタグと通常タグを両方抜き出せるようにしてあるもの。ちなみにコメントのマッチ正規表現に変更はない。convTag で使っているのはタグの内部だけにマッチする物。

 もうちょっと具体的に書くと、$tag_regex_ がタグ内の要素の一つにマッチするもの。$tag_regex がコメントもしくは < で始まり要素が1個以上あって > で終わっている物にマッチする。

 最初は $tag_regex しか使っていなかった。$tag_regex_ の定義で中に括弧を書いておけば、そこにマッチする物が順番に配列となって帰ってくるのだと思っていた。たとえば $html が <font size="5" color="red"> だとしたら、

@res = $html =~ /$tag_regex/;

 とすると、@res = qw(font size = "5" color = "red") という様な事になるのかと思っていた(これは実際の内容とは異なるが)。しかしやってみると qw(color = "red") みたいなことになっていた。要するに最後の要素しか入っていなかった。これでは使い物にならないと言うことで二つに分けた。ほぼ同じ正規表現でマッチさせるのはちと気が引けたが、取り出せないんだから仕方がない。

 さて、実際の関数の中身を見てみる。27行目で grep を使っているが、これは空の要素を排除するため。正規表現の中に key= があるか、もしくは無いか、という部分がある。key の部分と = の部分を括弧にしているからここが $1 $2 となる。ここが存在した場合はそれぞれが入るが、存在しなかった場合はこの二つは空になる。それを取り除こうというわけだ。

 そして36行目〜56行目までが許可されている属性だけを取り出している部分。

 要素を一つずつ取り出していって、= ならば次の要素である「値」も取り出す。

 要素がスペースのみで構成されている物ではなく、許可されていない属性であれば直前にあるはずのスペースを取り除く。これをしないとスペースがだぶってしまう。

 そしてその属性が key=value の形の物かどうかをチェック。次の要素が = ならばそうであると判断し、さらにその次の要素まで読み飛ばす。そうでなかった場合は配列に戻す。

 57行目。そもそも許可されていないタグであれば、要素はそのまま通す。

 あとはタグの閉じ括弧を入れ、配列を連結。そして不許可なタグならば含まれる < > を全て変換しておわり。

 ソースはここ