2010年12月20日月曜日

正規表現でタグの外側だけ対象に置換する

JavaScriptで、タグの外側だけを置換する。を読んで(これっPerlならどうするの?)っと思ったものの自力では無理でした。
if~else~でやろうとしたこと事態が初心者 。・゚・(つД`)・゚・。

ググリ方が悪くなかなかヒットせず、試しに「タグの外側だけを置換」(マンマ)で検索したら見事ヒット。

Perl正規表現雑技にありました。

正規表現で(?:\G|>)[^<]*?

と書くのですが、?:\Gが全く持って読めなかったので、ググリながらメモ。
タグが閉じたところから次のタグが始まる前の場所はタグの外側である.つまり, >[^<]*? である.
これは理解できる。が、ここから躓く。
また,前回置換した場所も タグの外側を置換したのであるから当然タグの外側である.その場所から次のタグが 始まる前の場所もタグの外側である.つまり, \G[^<]*? である.
\Gの意味を調べてみた。

正規表現メモには
文字列の先頭もしくは(グローバル指定時の)前のマッチが終了した 位置にマッチします。

正規表現 /g と \Gを読んで何となく分かった気になる。

次に?:

こいつはせうぞーさんのEPUB版『InDesign者のための正規表現入門』にあった。

グループのキャプチャをしない[検索のみ]

ということです。ループさせないために?:\Gを使っているのか? 続きを読むと。
ところが,文頭からタグが始まる場合に修飾子 g をつけて繰り返し置換させようとするとまずいことになる. 文頭からタグが始まる場合 ^()(?=<) が最初に マッチする.つまり,文頭の < の直前の隙間の空文字列がマッチし,パターンマッチの開始場所は最初の地点から動かない.これでは次に 修飾子 g によって再度置換しにいったとしても 同じ位置で永久に繰り返すことになってしまう.そこで,Perl では空文字列に マッチするような場合には,初回は空文字列がマッチするがそれ以降は マッチせずに必ず 1文字分は進むようにマッチしようとする. これにより永久に繰り返すことを防いでいるわけだが,その結果,今度は .*? が文頭の < を含むようになり, 一旦文頭の < を含んでしまったら,文末か次の < が来るまで伸び続け,結局文頭から始まるタグを含んでマッチしてしまうことになる.

^()(?=<) が出てきたので読み解く

^()は先頭のグループ化された何もない文字列

?=は肯定先読み

つまり[\d,]+(?=円)が「(?=円)」をアンカー位置として「[\d,]+」がマッチした場合だけ検索が成功するがごとく、< の直前の隙間の空文字列にマッチする。(< の直前の隙間の空文字列というのがよく分からんのだが)

ところが続きを読むと?:\Gの説明ではないのです (-"-;
そこで,.*? が < を含まない ようにすることを考えると,[^<]*? となる. この時点で最初の正規表現はもう少し簡単になることがわかる.つまり, ([^<]*?)(?:$|(?=<)) というのは ([^<]*) とすることができる.

理解するにはまだまだ時間がかかりそうです。

0 件のコメント: