web-dev-qa-db-ja.com

置換文字列の後方参照構文(ドル記号を使用する理由)

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ではなく「正しい」ステートメントである場合、構文はより合理化されませんか?
47

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

いいえ。Perlはそれを使用しています。Perlは確かにJavaのPatternクラスよりも古いものです。 Javaのregexサポートは、Perl正規表現の観点から明示的に説明されています。

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

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

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

$\よりも適切な選択肢であると見なされた可能性がある別の可能性理由があります。つまり、\は、Java文字列リテラルでは\\として記述する必要があります。

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

33
Stephen C

いくつかの調査を行った後、私は今問題を理解しました:Perlhadがパターンの後方参照と置換の後方参照に別のシンボルを使用し、Java.util.regex.*はthave追随する、それは技術的ではなくむしろ伝統的な理由のために選択します。


Perl側

(この時点で私がPerlについて知っていることはすべて、Wikipediaの記事を読んでいることなので、私が犯したかもしれない間違いを遠慮なく訂正してください)

Perlでこのように行われる必要がある理由は次のとおりです。

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

したがって、Perlの解釈方法とその正規表現エンジンの動作のため、シギル\1が代わりに使用されると(たとえば$)、意図しない変数が発生するため、パターン内のバックリファレンス(たとえば$1)の前にスラッシュを使用する必要があります。パターンへの補間。

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

参考文献


Java側

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

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

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

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

18