393

正規表現に関するこのチュートリアルを見つけました。「貪欲」、「消極的」、および「独占的」修飾子が何をするかを直感的に理解していますが、私の理解には深刻な穴があるようです。

具体的には、次の例です。

Enter your regex: .*foo // Greedy qualifier
Enter input string to search: xfooxxxxxxfoo
I found the text "xfooxxxxxxfoo" starting at index 0 and ending at index 13.

Enter your regex: .*?foo // Reluctant qualifier
Enter input string to search: xfooxxxxxxfoo
I found the text "xfoo" starting at index 0 and ending at index 4.
I found the text "xxxxxxfoo" starting at index 4 and ending at index 13.

Enter your regex: .*+foo // Possessive qualifier
Enter input string to search: xfooxxxxxxfoo
No match found.

説明では、入力文字列全体を食べる、文字が消費される、マッチャーが後退する、右端の「foo」が逆流するなどについて言及しています。

残念ながら、素晴らしい比喩にもかかわらず、私はまだ誰が何を食べているのか理解できません...正規表現エンジンがどのように機能するかを (簡潔に) 説明している別のチュートリアルを知っていますか?

または、誰かが次の段落を多少異なる言い回しで説明できれば、それは大歓迎です。

最初の例では、貪欲な量指定子を使用.*して、文字"f", "o", "o". 量指定子は貪欲であるため.*、式の部分は最初に入力文字列全体を消費します。この時点で、最後の 3 文字 ( "f""o""o") は [誰によって?] 既に消費されているため、式全体は成功しません。そのため、マッチャーは [右から左へ?] 1 文字ずつゆっくりとバックオフし、右端の出現"foo"が逆流されるまで [これはどういう意味ですか?]、その時点で一致が成功し、検索が終了します。

ただし、2 番目の例は消極的であるため、最初に [誰が?] "何も" 消費しないことから始めます。"foo"は文字列の先頭に表示されないため、最初の文字 (an "x") を飲み込む [誰が飲み込むのか?] ことが強制され、0 と 4 で最初の一致がトリガーされます。テスト ハーネスは、入力文字列が使い果たされるまでプロセスを続けます。 . 4 と 13 で別の一致が見つかります。

3 番目の例は、量指定子が所有格であるため、一致を見つけることができません。この場合、入力文字列全体が.*+[how?] によって消費され、式の末尾にある「foo」を満たすために何も残りません。後戻りせずに何かのすべてをつかみたい状況では、所有量指定子を使用します [後戻りとはどういう意味ですか?]。一致がすぐに見つからない場合は、同等の貪欲な量指定子よりも優れています。

4

7 に答える 7

553

やってみます。

貪欲な量指定子は、最初に可能な限り一致します。したがって、.*は文字列全体に一致します。その後、マッチャーは次のマッチングを試みfますが、文字が残っていません。したがって、「バックトラック」して、貪欲な量指定子を 1 文字少なく一致させます (文字列の末尾の「o」は一致しません)。それはまだ正規表現の と一致しないfので、もう 1 ステップバックトラックし、貪欲な量指定子が再び 1 文字少なく一致するようにします (文字列の末尾の「oo」は一致しません)。それでも正規表現の the と一致しないfため、もう 1 ステップバックトラックします (文字列の末尾にある "foo" は一致しません) 。これで、マッチャーは最終的fに正規表現のooもマッチしています。成功!

が進まない、または「貪欲でない」量指定子は、最初は可能な限り一致しません。したがって、.*最初は何も一致せず、文字列全体が一致しません。その後、マッチャーはf次のものと一致させようとしますが、文字列の一致しない部分が "x" で始まるため、機能しません。そのため、マッチャーはバックトラックし、貪欲ではない量指定子をもう 1 文字一致させます (今度は "x" に一致し、"fooxxxxxxfoo" は一致しません)。次に、 との一致を試みますがf、これは成功し、正規表現oの と 次も一致します。o成功!

あなたの例では、同じプロセスに従って、文字列の残りの一致しない部分「xxxxxxfoo」でプロセスを最初からやり直します。

所有量指定子は貪欲な量指定子と同じですが、バックトラックしません。そのため、文字列全体を照合することから始め、.*一致しないものは何も残しません。f次に、正規表現の と一致するものは何も残っていません。所有量指定子はバックトラックしないため、一致はそこで失敗します。

于 2011-03-16T01:22:08.730 に答える
24

「逆流する」または「後退する」という正確な用語は聞いたことがありません。これらを置き換えるフレーズは「後戻り」ですが、「後戻りする前に一時的に受け入れられたコンテンツが再び破棄される」には、「逆流」が最も適しているように思えます。

ほとんどの正規表現エンジンについて認識すべき重要な点は、それらがバックトラッキングを行っているということです。それらは、正規表現の内容全体を照合しようとしながら、潜在的な部分一致を暫定的に受け入れます。最初の試行で正規表現を完全に一致させることができない場合、正規表現エンジンはその一致の 1 つをバックトラックします。*+?、交互、または{n,m}繰り返しのマッチングを別の方法で試行し、再試行します。(もちろん、このプロセスには時間かかる場合があります。)

最初の例では、貪欲な量指定子 .* を使用して、「何か」を 0 回以上検索し、その後に「f」「o」「o」の文字が続きます。量指定子は貪欲であるため、式の .* 部分は最初に入力文字列全体を消費します。この時点では、最後の 3 文字 ("f" "o" "o") が既に消費されているため (誰によって? )、式全体は成功しません。

最後の 3 文字、、、fおよびoは、ルールの最初の部分でo既に使用されています。.*ただし、正規表現の次の要素である にfは、入力文字列に何も残っていません。エンジンは、最初の一致でバックトラックを強制され.*、最後の文字以外のすべての文字との一致を試みます。(3 つの文字通りの用語があるため、最後の 3 つを除くすべてに戻るのが賢明かもしれませんが、このレベルでの実装の詳細については知りません。)

そのため、マッチャーはゆっくりと (右から左へ? ) 1 文字ずつ「foo」の出現が逆流するまで (これはどういう意味ですか? )、

これは、マッチング時に暫定的fooに が含まれていたことを意味します。その試行が失敗したため、正規表現エンジンは で 1 つ少ない文字を受け入れようとします。この例の前に成功した一致があった場合、エンジンはおそらく一致を短縮しようとします(指摘したように、それは貪欲な修飾子であるため、右から左へ)、一致できなかった場合入力全体の場合、私の仮説の例で以前に一致したものを再評価する必要があるかもしれません。.*.*.*.*.*

ポイント一致が成功し、検索が終了します。

ただし、2 番目の例は消極的であるため、最初に (誰が? ) 「何も」消費しないことから始めます。「ふー」だから

最初の何も消費されない によって.?*、正規表現の残りの部分が一致することを可能にする、可能な限り最小限の量が消費されます。

文字列の先頭に表示されない場合、強制的に飲み込まれます (誰が飲み込むのですか?)

.?*正規表現全体を可能な限り最短の一致に一致させるために最初の失敗をバックトラックした後、再び最初の文字を消費します。(この場合、正規表現エンジンは の一致を.*?左から右に拡張しています。これは、.*?が消極的であるためです。)

最初の文字 (「x」) は、0 と 4 で最初の一致をトリガーします。テスト ハーネスは、入力文字列が使い果たされるまでプロセスを続行します。4 と 13 で別の一致が見つかります。

3 番目の例は、量指定子が所有格であるため、一致を見つけることができません。この場合、入力文字列全体が .*+ によって消費されます (どのように? )

A.*+は可能な限り消費し、正規表現全体が一致を見つけられない場合に新しい一致を見つけるためにバックトラックしません。. _ .*+_account: [[:digit:]]*+ phone: [[:digit:]]*+

入力が一致しない場合、潜在的な一致を決してバックトラックしないように正規表現エンジンに指示しているため、これにより正規表現一致が大幅に高速化されます。(一致するコードをすべて手で書かなければならない場合、これはputc(3)、入力文字を「プッシュバック」するために決して使用しないことに似ています。これは、最初の試行で書く可能性のある単純なコードに非常に似ています。正規表現エンジンを除いてプッシュバックの1文字よりもはるかに優れており、すべてをゼロに巻き戻して再試行できます.:)

しかし、潜在的なスピードアップだけでなく、これにより、一致する必要があるものと正確に一致する正規表現を記述できるようになります。簡単な例を思い付くのに苦労しています:)しかし、所有格と貪欲な量指定子を使用して正規表現を作成すると、異なる一致が得られる可能性があり、どちらかがより適切な場合があります。

式の最後にある「foo」を満たすために何も残さないでください。後戻りせずに何かのすべてをつかみたい状況では、所有量指定子を使用します (「後戻り」とはどういう意味ですか? )。それは上回ります

このコンテキストでの「バックオフ」とは、「バックトラック」を意味します。一時的な部分一致を破棄して、成功する場合と失敗する場合がある別の部分一致を試みることです。

一致がすぐに見つからない場合は、同等の貪欲な量指定子。

于 2011-03-16T01:24:45.843 に答える
20

http://swtch.com/~rsc/regexp/regexp1.html

それがインターネット上での最良の説明であるかどうかはわかりませんが、かなりよく書かれており、適切に詳細に説明されているため、何度も戻ってきます. あなたはそれをチェックしたいかもしれません。

より高度な (あまり詳細でない説明) が必要な場合は、ここで見ているような単純な正規表現について、正規表現エンジンがバックトラックによって機能します。基本的に、文字列のセクションを選択 (「食べる」) し、そのセクションに対して正規表現を照合しようとします。それが一致する場合は、素晴らしいです。そうでない場合、エンジンは文字列のセクションの選択を変更し、可能な限りすべての選択を試みるまで、そのセクションに対して正規表現を一致させようとします。

このプロセスは再帰的に使用されます。文字列を特定の正規表現と照合する際に、エンジンは正規表現を断片に分割し、アルゴリズムを各断片に個別に適用します。

エンジンが文字列のどの部分と照合するかを選択し、それが最初に機能しない場合にその選択を変更する方法を決定するときに、貪欲、消極的、および所有格の量指定子の違いが現れます。ルールは次のとおりです。

  • 貪欲な量指定子は、文字列全体(または少なくとも、正規表現の前の部分とまだ一致していないすべての文字列) から開始し、正規表現と一致するかどうかをチェックするようにエンジンに指示します。もしそうなら、素晴らしいです。エンジンは残りの正規表現を続行できます。そうでない場合は、再試行しますが、チェックする文字列のセクションから 1 文字 (最後の文字) を削除します。それが機能しない場合は、別の文字を削除するなどです。したがって、貪欲な量指定子は、可能な一致を最長から最短の順にチェックします。

  • 気が進まない量指定子は、文字列の可能な限り短い部分から開始するようにエンジンに指示します。一致する場合、エンジンは続行できます。そうでない場合は、チェックされている文字列のセクションに 1 文字を追加して試行し、一致する文字列が見つかるか、文字列全体が使い果たされるまで繰り返します。したがって、気が進まない量指定子は、可能な一致を最短から最長の順にチェックします。

  • 所有量指定子は、最初の試行では貪欲な量指定子のようなものです。つまり、文字列全体をチェックして開始するようにエンジンに指示します。違いは、それが機能しない場合、所有格量指定子はその場で一致が失敗したことを報告することです。エンジンは、調べている文字列のセクションを変更せず、それ以上試行しません。

これが、あなたの例で所有量指定子の一致が失敗する理由です。一致する.*+文字列全体に対してチェックされますが、エンジンはその後追加の文字を探し続けますfooが、もちろんそれらは見つかりません。 ' はすでに文字列の末尾にあります。それが貪欲な量指定子である場合、バックトラックして、.*最後から 2 番目の文字まで、次に最後から 3 番目の文字まで、次に最後から 4 番目の文字までの唯一の一致を試みます。が文字列の前の部分を「食べた」foo後に残っています。.*

于 2011-03-16T01:25:06.560 に答える
15

これが、Cell と Index の位置を使用した私の見解です (Cell と Indexを区別するには、ここの図を参照してください)。

貪欲 - 貪欲な量指定子と正規表現全体に可能な限り一致します。一致しない場合は、貪欲な量指定子に戻ります。

入力文字列: xfooxxxxxxfoo 正規表現
: .*foo

上記の正規表現には
、(i)'.*' と
(ii)'foo'

の 2 つの部分があります。以下の各手順では、2 つの部分を分析します。「合格」または「不合格」への一致に関する追加のコメントは、中かっこ内で説明されています。

ステップ 1:
(i) .* = xfooxxxxxxfoo - PASS ('.*' は貪欲な量指定子であり、入力文字列全体を使用します)
(ii) foo = インデックス 13 の後に一致する文字が残っていません - FAIL
一致に失敗しました。

ステップ 2:
(i) .* = xfooxxxxxxfo - PASS (貪欲な量指定子 '.*' のバックトラック)
(ii) foo = o - FAIL
一致に失敗しました。

ステップ 3:
(i) .* = xfooxxxxxxf - 合格 (貪欲な量指定子 '.*' のバックトラック)
(ii) foo = oo - FAIL
一致に失敗しました。

ステップ 4:
(i) .* = xfooxxxxxx - PASS (貪欲な量指定子 '.*' のバックトラック)
(ii) foo = foo - PASS
Report MATCH

結果: 1 件の一致
インデックス 0 から始まり、インデックス 13 で終わるテキスト "xfooxxxxxxfoo" を見つけました。

Reluctant - reluctant 量指定子に可能な限り一致せず、正規表現全体に一致します。一致しない場合は、消極的な量指定子に文字を追加します。

入力文字列: xfooxxxxxxfoo 正規表現
: .*?foo

上記の正規表現には 2 つの部分があります:
(i) '.*?' および
(ii) 'foo'

ステップ 1:
.*? = '' (空白) - 合格 (消極的な量指定子 '.*?' にはできるだけ一致しません。'' を持つインデックス 0 は一致します。)
foo = xfo - FAIL (セル 0,1,2 - つまり、間のインデックス0 および 3)
一致は失敗しました。

ステップ 2:
.*? = x - PASS (消極的な量指定子 '.*?' に文字を追加します。'x' を持つセル 0 が一致します。)
foo = foo - PASS
Report MATCH

ステップ 3:
.*? = '' (空白) - 合格 (消極的な量指定子 '.*?' にはできるだけ一致しません。'' を持つインデックス 4 は一致します。)
foo = xxx - FAIL (セル 4,5,6 - つまり、間のインデックス4 と 7)
一致は失敗しました。

ステップ 4:
.*? = x - PASS (消極的な量指定子 '.*?' に文字を追加します。セル 4。)
foo = xxx - FAIL (セル 5、6、7 - すなわち 5 と 8 の間のインデックス)
一致に失敗しました。

ステップ 5:
.*? = xx - PASS (消極的な量指定子 '.*?' に文字を追加します。セル 4 から 5 まで。)
foo = xxx - FAIL (セル 6、7、8 - すなわち 6 と 9 の間のインデックス)
一致に失敗しました。

ステップ 6:
.*? = xxx - PASS (消極的な量指定子 '.*?' に文字を追加します。セル 4 から 6 まで。)
foo = xxx - FAIL (セル 7、8、9 - すなわち 7 と 10 の間のインデックス)
一致に失敗しました。

ステップ 7:
.*? = xxxx - PASS (消極的な量指定子 '.*?' に文字を追加します。セル 4 から 7 まで。)
foo = xxf - FAIL (セル 8、9、10 - すなわち 8 と 11 の間のインデックス)
一致は失敗しました。

ステップ 8:
.*? = xxxxx - 合格 (消極的な量指定子 '.*?' に文字を追加します。セル 4 から 8 まで。)
foo = xfo - 不合格 (セル 9、10、11 - すなわち 9 と 12 の間のインデックス)
一致は失敗しました。

ステップ 9:
.*? = xxxxxx - PASS (消極的な量指定子 '.*?' に文字を追加します。セル 4 から 9 まで。)
foo = foo - PASS (セル 10、11、12 - すなわち 10 と 13 の間のインデックス)
MATCH を報告します。

ステップ 10:
.*? = '' (空白) - 合格 (消極的な量指定子 '.*?' にはできるだけ一致しません。インデックス 13 は空白です。)
foo = 一致する文字が残っていません - 失敗 (インデックス 13 の後に一致するものはありません)
一致失敗した。

結果: 2 件の一致
インデックス 0 から始まり、インデックス 4
で終わるテキスト "xfoo" が見つかりました。インデックス 4 から始まり、インデックス 13 で終わるテキスト "xxxxxxfoo" が見つかりました。

所有 - 可能な限り所有量指定子に一致し、正規表現全体に一致します。バックトラックしないでください。

入力文字列: xfooxxxxxxfoo 正規表現
: .*+foo

上記の正規表現には、「.*+」と「foo」の 2 つの部分があります。

ステップ 1:
.*+ = xfooxxxxxxfoo - PASS (所有量指定子 '.*' に可能な限り一致)
foo = 一致する文字が残っていない - FAIL (インデックス 13 の後に一致するものがない)
一致に失敗しました。

注:バックトラッキングは許可されていません。

結果: 0 試合

于 2015-02-04T00:28:58.050 に答える
2

貪欲:「可能な限り長い文字列に一致する」

気が進まない: 「可能な限り短い文字列に一致する」

Possessive: これは少し奇妙です (貪欲で消極的とは対照的に) 正規表現全体の一致を見つけようとしないからです。

ところで、バックトラッキングを使用する正規表現パターン マッチャーの実装はありません。すべての実際のパターン マッチャーは非常に高速です。正規表現の複雑さとはほとんど関係ありません。

于 2013-09-03T14:45:45.673 に答える