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 の正規表現を理解するための良い入門書になるかもしれません。