4

欲張りでない正規表現(regex)に問題があります。欲張りでない正規表現に関する質問があるのを見てきましたが、それらは私の問題に答えません。

問題:「lol」アンカーのhrefを一致させようとしています。

注:これはPerl HTML解析モジュールで実行できることを知っています。私の質問は、PerlでのHTMLの解析に関するものではありません。私の質問は正規表現自体についてであり、HTMLは単なる例です。

テストケース:との4つのテストが.*?あり[^"]ます。2つは、最初に期待される結果を生成します。しかし、3番目はそうではなく、4番目はちょうどそうですが、理由はわかりません。

  1. .*?との両方のテストで3番目のテストが失敗するのはなぜ[^"]ですか?貪欲でないオペレーターは機能すべきではありませんか?
  2. .*?との両方のテストで4番目のテストが機能するのはなぜ[^"]ですか?前に含めると正規表現が変わる理由がわかりません.*(3番目と4番目のテストは前を除いて同じ.*です)。

私はおそらく、これらの正規表現がどのように機能するかを正確に理解していません。Perlクックブックレシピは何かについて言及していますが、それが私の質問に答えるとは思いません。

use strict;

my $content=<<EOF;
<a href="/hoh/hoh/hoh/hoh/hoh" class="hoh">hoh</a>
<a href="/foo/foo/foo/foo/foo" class="foo">foo </a>
<a href="/bar/bar/bar/bar/bar" class="bar">bar</a>
<a href="/lol/lol/lol/lol/lol" class="lol">lol</a>
<a href="/koo/koo/koo/koo/koo" class="koo">koo</a>
EOF

print "| $1 | \n\nThat's ok\n" if $content =~ m~href="(.*?)"~s ;

print "\n---------------------------------------------------\n";

print "| $1 | \n\nThat's ok\n" if $content =~ m~href="(.*?)".*>lol~s ;

print "\n---------------------------------------------------\n";

print "| $1 | \n\nWhy does not the 2nd non-greedy '?' work?\n"
  if $content =~ m~href="(.*?)".*?>lol~s ;

print "\n---------------------------------------------------\n";

print "| $1 | \n\nIt now works if I put the '.*' in the front?\n"
  if $content =~ m~.*href="(.*?)".*?>lol~s ;

print "\n###################################################\n";
print "Let's try now with [^]";
print "\n###################################################\n\n";


print "| $1 | \n\nThat's ok\n" if $content =~ m~href="([^"]+?)"~s ;

print "\n---------------------------------------------------\n";

print "| $1 | \n\nThat's ok.\n" if $content =~ m~href="([^"]+?)".*>lol~s ;

print "\n---------------------------------------------------\n";

print "| $1 | \n\nThe 2nd greedy still doesn't work?\n"
  if $content =~ m~href="([^"]+?)".*?>lol~s ;

print "\n---------------------------------------------------\n";

print "| $1 | \n\nNow with the '.*' in front it does.\n"
  if $content =~ m~.*href="([^"]+?)".*?>lol~s ;
4

4 に答える 4

6

(正規$&表現全体に一致するテキスト) と$1. これにより、何が起こっているのかをよりよく理解できる場合があります。

あなたが抱えていると思われる問題は、.*?「ここで最も少ない文字を使用するすべての可能な一致から一致を見つける」という意味ではないということです。それは単に、「まず、ここで 0 文字のマッチングを試み、続けて正規表現の残りのマッチングを試みます。それが失敗した場合は、1 文字のマッチングを試みます。残りの正規表現が一致しない場合は、ここで 2 文字のマッチングを試みます。」などを意味します。 "

Perl は常に、文字列の先頭に最も近いところから始まる一致を見つけます。ほとんどのパターンは で始まるため、文字列href=の最初のパターンを見つけて、href=そこから一致するように繰り返しを拡張する方法があるかどうかを確認します。一致しない場合は、次の から始めようとしますhref=

.*regex の先頭にgreedy を追加すると、.*できるだけ多くの文字を取得することからマッチングが開始されます。次に、Perl はバックトラックしてhref=. 基本的に、これにより文字列の最後 href=が最初に試行され、文字列の先頭に向かって動作します。

于 2011-05-14T10:29:48.727 に答える
0

4番目のテストケースのみが機能しています。

最初:m~href="(.*?)"~s

これは、文字列内の最初のhrefと一致し、引用符の間にあるものをキャプチャします。/hoh/hoh/hoh/hoh/hoh

二番目:m~href="(.*?)".*>lol~s

これは、文字列内の最初のhrefと一致し、引用符の間にあるものをキャプチャします。次に、それが見つかるまで、任意の数の任意の文字と一致します>lol/hoh/hoh/hoh/hoh/hoh

でキャプチャしてみて.*くださいm~href="(.*?)"(.*)>lol~s

$1 contains:
/hoh/hoh/hoh/hoh/hoh
$2 contains: 
class="hoh">hoh</a>
<a href="/foo/foo/foo/foo/foo" class="foo">foo </a>
<a href="/bar/bar/bar/bar/bar" class="bar">bar</a>
<a href="/lol/lol/lol/lol/lol" class="lol" 

第3:m~href="(.*?)".*?>lol~s

前のテストケースと同じ結果。

第4:m~.*href="(.*?)".*?>lol~s

これは、任意の数の任意の文字と一致し、次にhref="、引用符まで貪欲でない任意の数の任意の文字をキャプチャし、それが見つかるまで任意の数の任意の文字と一致します>lol/lol/lol/lol/lol/lol

.*ですべてをキャプチャしてみてくださいm~(.*)href="(.*?)"(.*?)>lol~s

$1 contains:
<a href="/hoh/hoh/hoh/hoh/hoh" class="hoh">hoh</a>
<a href="/foo/foo/foo/foo/foo" class="foo">foo </a>
<a href="/bar/bar/bar/bar/bar" class="bar">bar</a>
<a
$2 contains: 
/lol/lol/lol/lol/lol
$3 contains:
class="lol"

このサイトを見て、正規表現が何をしているのかを説明しています。

于 2011-05-14T10:03:32.857 に答える
0

ここで何が起こっているのかを説明してみましょう(なぜそれが起こるのか他の答えを見てください):

href="(.*?)"

試合:href="/hoh/hoh/hoh/hoh/hoh"
グループ:/hoh/hoh/hoh/hoh/hoh

href="(.*?)".*>lol

マッチ:href="/hoh/hoh/hoh/hoh/hoh" class="hoh">hoh</a> <a href="/foo/foo/foo/foo/foo" class="foo">foo </a> <a href="/bar/bar/bar/bar/bar" class="bar">bar</a> <a href="/lol/lol/lol/lol/lol" class="lol">lol

グループ:/hoh/hoh/hoh/hoh/hoh

href="([^"]+?)".*?>lol

マッチ:href="/hoh/hoh/hoh/hoh/hoh" class="hoh">hoh</a> <a href="/foo/foo/foo/foo/foo" class="foo">foo </a> <a href="/bar/bar/bar/bar/bar" class="bar">bar</a> <a href="/lol/lol/lol/lol/lol" class="lol">lol

グループ:/hoh/hoh/hoh/hoh/hoh

.*href="(.*?)".*?>lol

マッチ:<a href="/hoh/hoh/hoh/hoh/hoh" class="hoh">hoh</a> <a href="/foo/foo/foo/foo/foo" class="foo">foo </a> <a href="/bar/bar/bar/bar/bar" class="bar">bar</a> <a href="/lol/lol/lol/lol/lol" class="lol">lol

グループ:/lol/lol/lol/lol/lol

必要な正規表現を記述する1 つの方法は、次を使用することです。href="[^"]*"[^>]*>lol

于 2011-05-14T11:06:48.540 に答える
0

主な問題は、すべきではないときに貪欲でない正規表現を使用していることです。2 番目の問題は、 which を使用.すると、意図したよりも多く一致する可能性があることです。あなたが使用しているフラグは、*さらに一致します。s.

使用する:

m~href="([^"]+)"[^>]*>lol~

あなたの場合。貪欲でない正規表現については、次のコードを検討してください。

$_ = "xaaaaab xaaac xbbc";
m~^x.+?c~;

ご想像のとおり、「xaaac」には一致しません。文字列の先頭から開始し、「xaaaaab xaaac」に一致します。貪欲なバリアントは、文字列全体に一致します。

要点は、非貪欲な正規表現は可能な限り取得しようとはしませんが、貪欲な兄弟と同じ熱心さでなんとか一致させようとすることです。そして、彼らはそれを行うために文字列のあらゆる部分をつかみます.

また、バックトラッキングをオフにする「所有」量指定子を検討することもできます。

また、クックブックは始めるのに適していますが、物事が実際にどのように機能するかを理解したい場合は、これを読む必要があります - perlre .

于 2011-05-14T10:36:32.290 に答える