7

所有量指定子は貪欲で、バックトラックを拒否します。正規表現の/.{1,3}+b/意味: 改行を除く任意の文字に 1 ~ 3 回、できるだけ多く一致し、バックトラックしない。次に、文字を一致させますb

この例では:

'ab'.sub /.{1,3}+b/, 'c'    #=> "c"

事実に反して、置換は行われるべきではありません。

これら 2 つの例の結果は異なります。

'aab'.sub /.{0,1}+b/, 'c'   #=> "c"
'aab'.sub /.?+b/, 'c'       #=> "ac"

これを Scala と比較すると、同じ答えが得られます。

scala> ".{0,1}+b".r.replaceAllIn("aab", "c")
res1: String = ac
scala> ".?+b".r.replaceAllIn("aab", "c")
res2: String = ac

これは Ruby のバグですか、それともこの動作を動機付けることができますか? おそらく、鬼車は何らかの理由で、総称量指定子を除くすべての量指定子?で所有格を実装したのでしょうか? もしそうなら、なぜですか?*+{m,n}

4

2 に答える 2

5

実際に何が起こるか

+範囲数量詞が続くと、範囲数量詞に所有格が提供されないようです。むしろ、前に1回以上繰り返されたものとして扱われます。.{1,3}+b例として使用すると、と同等になります(?:.{1,3})+b

回避策

これは、より一般的なコンストラクトの非バックトラッキンググループ(またはアトミックグループ化)で回避でき(?>pattern)ます。pattern{n,m}+一般的なケースを例として使用して、非バックトラッキンググループを使用して同等の正規表現を作成しましょう(と一致する場合のJavaの動作に相当しますpattern{n,m}+)。

(?>(?>pattern){n,m})

なぜ2レベルの非バックトラッキンググループなのですか?2が必要な理由:

  • (繰り返しの1つのインスタンス)に一致するものが見つかった場合、pattern内部でのバックトラックpatternは許可されません。(インスタンスが見つからない限り、内部でのバックトラックpatternが許可されることに注意してください)。これは、内部の非バックトレースグループでエミュレートされます。
  • のインスタンスがこれ以上pattern見つからない場合、インスタンスを削除するためのバックトラックは許可されません。これは、外側の非バックトラッキンググループでエミュレートされます。

ここに注意点があるかどうかはわかりません。この方法でエミュレートされていないケースを見つけた場合は、コメントで私にpingしてください。

テスト

テスト1

最初に、私はこの正規表現をテストしました:

(.{1,3}+)b

最初はキャプチャグループなしでテストしましたが、結果が非​​常に驚くべきものだったため、キャプチャグループで何が起こっているのかを確認する必要がありました。

この入力について:

2343333ab

その結果、文字列全体がに一致し、キャプチャグループがキャプチャされます2343333ab末尾にがありません)。これは、上限がなんらかの形で破られていることを示しています。

rubularでのデモ

テスト2

この2番目のテストは、範囲数量詞の動作を所有格{n}に変更できないことを明らかにします。これは、他の範囲数量詞とにも当てはまる可能性があります。代わりに、以下は1回以上の時間動作の繰り返しのみを示します。{n,}{n,m}+

(私の最初の結論は+、上限を上書きするということですが、それは間違っていることがわかりました)。

正規表現のテスト:

(.{3}+)b

入力文字列:

23d4344333ab
234344333ab
23434433ab

キャプチャグループ1でキャプチャされた一致は、すべて3の倍数です。上から下に、正規表現は入力文字列に対してそれぞれ2、1、0文字をスキップします。

注釈付きの入力文字列([]正規表現全体に一致することを()示し、グループ1をキャプチャすることによってキャプチャされたテキストを示します):

23[(d4344333a)b]
2[(34344333a)b]
[(23434433a)b]

rubularでのデモ

回避策のテストコード

これは、外部と内部の両方の非バックトラッキンググループが必要であることを示すJavaのテストコードです。イデオネ

class TestPossessive {
  public static void main(String args[]) {
    String inputText = "123456789012";
    System.out.println("Input string: " + inputText);
    System.out.println("Expected: " + inputText.replaceFirst("(?:\\d{3,4}(?![89])){2,}+", ">$0<"));
    System.out.println("Outer possessive group: " + inputText.replaceFirst("(?>(?:\\d{3,4}(?![89])){2,})", ">$0<"));
    System.out.println("Inner possessive group: " + inputText.replaceFirst("(?>\\d{3,4}(?![89])){2,}", ">$0<"));
    System.out.println("Both: " + inputText.replaceFirst("(?>(?>\\d{3,4}(?![89])){2,})", ">$0<"));

    System.out.println();

    inputText = "aab";
    System.out.println("Input string: " + inputText);
    System.out.println("Expected: " + inputText.replaceFirst(".{1,3}+b", ">$0<"));
    System.out.println("Outer possessive group: " + inputText.replaceFirst("(?>.{1,3})b", ">$0<"));
    System.out.println("Inner possessive group: " + inputText.replaceFirst("(?>.){1,3}b", ">$0<"));
    System.out.println("Both: " + inputText.replaceFirst("(?>(?>.){1,3})b", ">$0<"));
  }
}
于 2013-03-20T10:14:30.180 に答える
2

これは鬼車の意図のようです。ドキュメンテーションは言う{n,m}+, {n,}+, {n}+ are possessive op. in ONIG_SYNTAX_JAVA only。これは下位互換性の理由によるものだと思いますか、それとも?

于 2013-03-20T11:48:51.950 に答える