form
以下のコードを考えると、最初のオカレンスと一致させたいと思います。私はそれを達成するためにネガティブ先読み?!
が使用されるかもしれないことを知りましたが、それは機能しません。正規表現の何が問題になっていますか?
#test
$test = "<form abc> foo </form> <form gg> bar </form>";
$test =~ m/<form[^>]*abc[^>]*>(?!.*form>.*)form>/s;
print $&;
まず、正規表現を説明する前に、次のようなモジュールを使用HTML::TreeBuilder
してドキュメント ツリーを作成し、そこから情報を取得します。正規表現を使用して HTML を解析すると、現実の世界で使用するにはエラーが発生しやすくなります。
これがあなたの文字列です:
"<form abc> foo </form> <form gg> bar </form>"
そしてあなたの正規表現(/x
フラグと同様に、読みやすくするために拡張して書かれています):
<form [^>]* abc [^>]* > (?! .* form> .* ) form>
<form
リテラル文字シーケンスが見つかったときにアンカーします
[^>]*
多数の非>
文字を検索します。そもそも合ってる abc
abc
は、リテラル文字シーケンスに一致しますabc
。しかし、正規表現エンジンは現在 を認識し ているため、一致>
するまでバックトラックする必要があります。[^>]*
[^>]*
エンジンは>
>
一致する>
式が一致しない場合、否定先読みが一致し.* form .*
ます。
は.*
、文字列の最後まですべての文字を消費します。
form>
.*
一致するまでエンジンをバックトラックさせfoo </form> <form gg> bar </
ます。
は.*
何も一致しませんが、それで問題ありません。
したがって、先読みは成功しますが、それは否定的な先読みであるため、アサーションは失敗します。正規表現の最後の部分は実行されません。
私たちの場合、.*
はあまりにも多くの文字を消費します。これは貪欲なマッチングと呼ばれます。
非貪欲なマッチングは、末尾?
に likeを付けて記述し.*?
ます。このバージョンは、最初にゼロ文字を消費し、最初にパターンの次の部分をチェックします。それがうまくいかない場合は、一致するまで別の文字を繰り返し消費します。
<form [^>]* > .*? </form>
開始タグ内では、非>
文字のみが許可されます。タグの間には、任意の文字を使用できます。非貪欲なマッチングを行うため、最初の終了タグが一致して正規表現を終了します。
ただし、このソリューションには少し問題があります。寛容な HTML パーサーは、attr="val<u>e"
. 私達はします。また、最初の</form>
ものが一致しますが、これはネストされたフォームがある場合には望ましくありません。この使用例では問題はありませんが、この正規表現は<div>
s などを照合するときにはまったく役に立ちません。
Perl の正規表現は非常に強力で、再帰的な文法を宣言できます。組み込みの構文は少し厄介ですが、Regexp::Grammars
モジュールで簡単に実行できるようにすることをお勧めします。さらに良いのは、すでに出回っている本格的な HTML パーサーを使用することです。
$&
(and $`
and )の使用は$'
、perl の効率が非常に悪くなるため、お勧めできません。これは小さなスクリプトでは現れませんが、とにかくスタイルが悪いです。代わりに、一致をキャプチャするために、正規表現全体を括弧で囲みます
m{ ( <form [^>]* > .*? </form> ) }
を使用します$1
。
perlretut
チュートリアルは、Perl の正規表現を理解するための良い入門書になるかもしれません。