49

Java では、および他のいくつかの言語ではそう思われますが、パターン内の後方参照の前にはバックスラッシュ ( \1\2\3など) が付きますが、置換文字列ではドル記号 ( 、 、 など) が前に付きます ( $1$2$3、および$0)。

説明するスニペットを次に示します。

System.out.println(
    "left-right".replaceAll("(.*)-(.*)", "\\2-\\1") // WRONG!!!
); // prints "2-1"

System.out.println(
    "left-right".replaceAll("(.*)-(.*)", "$2-$1")   // CORRECT!
); // prints "right-left"

System.out.println(
    "You want million dollar?!?".replaceAll("(\\w*) dollar", "US\\$ $1")
); // prints "You want US$ million?!?"

System.out.println(
    "You want million dollar?!?".replaceAll("(\\w*) dollar", "US$ \\1")
); // throws IllegalArgumentException: Illegal group reference

質問:

  • 置換文字列での後方参照の使用は$Java に固有のものですか? そうでない場合、どの言語で開始されましたか? どのフレーバーがそれを使用し、どのフレーバーが使用しないのですか?
  • なぜこれが良い考えなのですか?同じパターン構文に固執しないのはなぜですか? それは、よりまとまりがあり、習得しやすい言語につながるのではないでしょうか?
    • 上記のステートメント 1 と 4 が 2 と 3 の代わりに「正しい」ステートメントである場合、構文はより合理化されませんか?
4

2 に答える 2

35

置換文字列の後方参照に $ を使用するのは、Java に固有のものですか?

いいえ、Perl はそれを使用しており、Perl は確かに Java のPatternクラスよりも前から存在しています。Java の正規表現サポートは、Perl 正規表現に関して明示的に記述されています。

例: http://perldoc.perl.org/perlrequick.html#Search-and-replace

なぜこれが良い考えなのですか?

明らかに、あなたはそれが良い考えだとは思いません! しかし、これが良い考えである理由の 1 つは、Java の検索/置換のサポートを Perl と (より) 互換にするためです。

よりも優れた選択肢と見なされた可能性ある別の理由が考えられます。つまり、Java String リテラルの ように記述する必要があります。$\\\\

しかし、これはすべて純粋な憶測です。設計の決定が下されたとき、私たちは誰も部屋にいませんでした。そして最終的には、なぜ彼らが置換文字列構文をそのように設計したのかは問題ではありません。決定は下され、具体的に設定されました。これ以上の議論は純粋に学術的なものです...新しい言語またはJava用の新しい正規表現ライブラリをたまたま設計している場合を除きます。

于 2010-05-23T05:03:03.017 に答える
20

いくつかの調査を行った後、私は今問題を理解しました: Perlパターン後方参照と置換後方参照に異なるシンボルを使用する必要があり、従う必要java.util.regex.*はありませんが、技術的ではなく伝統的な理由で使用することを選択しました。


パール側

(この時点で私が Perl について知っていることは、ウィキペディアの記事を読むことから得たものであることを覚えておいてください。そのため、間違いを犯した場合は自由に修正してください)

Perl でこのようにしなければならなかった理由は次のとおりです。

  • Perl は$シジル (つまり、変数名に付けられたシンボル) として使用します。
  • Perl 文字列リテラルは可変補間されます。
  • Perl regex は、実際にはグループを変数$1$2などとしてキャプチャします。

\1したがって、Perl が解釈される方法とその正規表現エンジンがどのように機能するかにより、パターン内の後方参照 (例: ) の前にスラッシュを$使用する必要があり$1ます。パターン。

置換文字列は、Perl での動作方法により、すべての一致のコンテキスト内で評価されます。ここで Perl が変数補間を使用するのは最も自然なことなので、正規表現エンジンはグループを変数$1$2などにキャプチャして、言語の残りの部分とシームレスに連携できるようにします。

参考文献


Java 側

Java は Perl とは非常に異なる言語ですが、ここで最も重要なことは、変数補間がないことです。さらに、replaceAllはメソッド呼び出しであり、Java のすべてのメソッド呼び出しと同様に、引数はメソッドが呼び出される前に 1 回評価されます。

したがって、本質的に置換文字列は一致ごとに再評価する必要があり、それはJavaのメソッド呼び出しのセマンティクスではないため、変数補間機能だけでは十分ではありません。が呼び出される前に評価される変数補間された置換文字列は、replaceAll実際には役に立ちません。補間は、すべての一致でメソッド中に発生する必要があります。

これは Java 言語のセマンティクスではないため、replaceAllこの「ジャストインタイム」補間を手動で行う必要があります。そのため、置換文字列で後方参照のエスケープ記号が使用される技術的な理由はまったくありません。$それは非常にうまくいった可能性があり\ます。逆に、パターン内の後方参照は の$代わりに でエスケープすることもでき\、技術的には問題なく機能していました。

Java がこのように正規表現を行う理由は、純粋に伝統的なものです。Perl によって設定された前例に従っているだけです。

于 2010-05-23T06:20:26.357 に答える