Perl覚え書き20041203

 さて、今回も例のアレに手を加える。しかもタグにマッチさせる正規表現を自分で作ってしまった。これまで使っていた物は、正しいタグの場合は正常にマッチするが…と思っていたら¥マークの付いた\"や\'は識別できないため、って書こうとしたら、

<a href="#" onclick="alert('abc\"def')">test</a>

 なんて書き方は不許可なのね…。abc\" の時点で文字列が終わったと見なされるため、javascriptの文法がおかしいって怒られてしまった。

 だったら正規表現を書き直す意味はないのかというとそんなことはない。はてなにメールを送って現在では修正されているが、一部に " の対応が取れていないタグがあった。これを前のバージョンに掛けると、かなり長いマッチをしてしまった。

 それは、" " と ' '、そして外側に任意の文字、という方法でチェックしていたため。それをある程度文法を調べるようにした。

 IEでのタグのチェック方法はこうじゃないか、と思われるのは以下の通り。

  • 先頭が " か ' でなく、スペースが来たら次にスペース以外が来るまでチェックし来たのが = ならばそこまでがキー(=前後のスペースは無視)
  • 先頭が " か ' の場合は、次に " か '(先頭と同じ文字)が来るまでが値
  • 同じく先頭が " か ' でない場合は、次にスペースか > が出てくるまでが値

 たったこれだけだ。それを正規表現で書くと次のようになる。

<(?:\s*(?:[^\s=]+\s*=\s*)?(?:"[^"]*"|'[^']*'|[^\s<>]+))*>

 これでは少し分かりづらいため分解してみる。

 1: <
 2:  (?:
 3:   \s*
 4:   (?:[^\s=]+\s*=\s*)?
 5:   (?:
 6:    "[^"]*"|
 7:    '[^']*'|
 8:    [^\s<>]+
 9:   )
10:  )*
11: >

 まず3行目。先頭にあるスペースは不要のため取り除く。4行目はkey=valueのkey=の部分にマッチする部分。ここは無くてもいいような気もするけど、まあ一応。28〜29行目は" "もしくは' 'の範囲にマッチ。30行目は先頭が " や ' でなかった場合はスペースか > が出てくるまでの文字がマッチ。ここまでの部分が0回以上繰り返される、ということになる。

 さて、通常はこれで問題ない。だけどこれだけではIEと同じ解析にはならない。ブラウザは、あるタグのある属性で、そこに出てくる文字種がどんなものであるかを当然知っている。

 たとえば次のような場合。

<font size="-2> (<font color="#0000ff">2</font>)</font></td>

 これをさきほどの解釈で分解すると、sizeの内容は、"-2 から color=" までと言うことになり、そして #0000ff" という属性があってこのタグは終わる。だけどもブラウザの解釈は違う。きちんとフォントサイズとフォントカラーのタグとして認識される。何故か。それはsizeの値に > が出てこないことをブラウザが知っているから。

 逆に値として > が出てくる可能性がある場合はこの正規表現通り、次に閉じの " か ' が出てくるまでが値となる。

 と言うことはつまり今回正規表現を書き換えたのははっきり言って意味がなかったんだなあと…がっかり。

 さてどうしよう。幸いこの関数はallow_tagsという連想配列を持っている。どのタグを有効にするか、そしてその中の有効にする属性はどれかという配列だが、属性が定義されているという事を表す為の値は全て1になっている。この数値に意味を持たせればいいんじゃないか。たとえばこれが1の場合は > が出てくる可能性はなし、2の場合は可能性ありという具合。

 それに関してはまた今度。