-2

form以下のコードを考えると、最初のオカレンスと一致させたいと思います。私はそれを達成するためにネガティブ先読み?!が使用されるかもしれないことを知りましたが、それは機能しません。正規表現の何が問題になっていますか?

#test
$test = "<form abc> foo </form> <form gg> bar </form>";
$test =~ m/<form[^>]*abc[^>]*>(?!.*form>.*)form>/s;
print $&;
4

1 に答える 1

7

まず、正規表現を説明する前に、次のようなモジュールを使用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 の正規表現を理解するための良い入門書になるかもしれません。

于 2012-08-19T22:19:00.247 に答える