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に固有ですか?そうでない場合、どの言語がそれを始めましたか?どのフレーバーがそれを使用し、何を使用しないのですか?置換文字列の後方参照に$を使用するのはJavaに固有ですか?
いいえ。Perlはそれを使用しています。Perlは確かにJavaのPattern
クラスよりも古いものです。 Javaのregexサポートは、Perl正規表現の観点から明示的に説明されています。
例: http://perldoc.Perl.org/perlrequick.html#Search-and-replace
なぜこれが良い考えなのですか?
まあ明らかにあなたはそれが良い考えだとは思わない!しかし、それが良い考えである1つの理由は、Perlと互換性のあるJava検索/置換サポート(もっと)にする)ことです。
$
が\
よりも適切な選択肢であると見なされた可能性がある別の可能性理由があります。つまり、\
は、Java文字列リテラルでは\\
として記述する必要があります。
しかし、これらはすべて純粋な推測です。設計上の決定がなされたとき、誰も部屋にいませんでした。そして最終的に、なぜ彼らが置換文字列構文をそのように設計したのかは本当に重要ではありません。決定は具体的に行われ、設定されました。Javaの新しい言語または新しい正規表現ライブラリを設計している場合を除いて、これ以上の議論は純粋に学術的なものです。
いくつかの調査を行った後、私は今問題を理解しました:Perlhadがパターンの後方参照と置換の後方参照に別のシンボルを使用し、Java.util.regex.*
はthave追随する、それは技術的ではなくむしろ伝統的な理由のために選択します。
(この時点で私がPerlについて知っていることはすべて、Wikipediaの記事を読んでいることなので、私が犯したかもしれない間違いを遠慮なく訂正してください)
Perlでこのように行われる必要がある理由は次のとおりです。
$
をシギル(つまり、変数名に付加されたシンボル)として使用します。$1
、$2
などとしてキャプチャします。したがって、Perlの解釈方法とその正規表現エンジンの動作のため、シギル\1
が代わりに使用されると(たとえば$
)、意図しない変数が発生するため、パターン内のバックリファレンス(たとえば$1
)の前にスラッシュを使用する必要があります。パターンへの補間。
Perlでの動作方法により、置換文字列はすべての一致のコンテキスト内で評価されます。 Perlがここで変数補間を使用するのが最も自然なため、正規表現エンジンはグループを変数$1
、$2
などにキャプチャして、これを言語の他の部分とシームレスに機能させます。
JavaはPerlとは非常に異なる言語ですが、最も重要なのは、変数の補間がないことです。さらに、replaceAll
はメソッド呼び出しであり、Javaのすべてのメソッド呼び出しと同様に、引数はメソッドが呼び出される前に一度評価されます。
したがって、本質的にはすべての一致で置換文字列を再評価する必要があるため、変数補間機能だけでは十分ではなく、Javaのメソッド呼び出しのセマンティクスではありません。評価される変数補間された置換文字列beforereplaceAll
が呼び出されても、実際には役に立たない。補間は発生する必要がありますduringすべての一致でメソッド。
Java言語のセマンティクスではないため、replaceAll
はこの「ジャストインタイム」補間手動を実行する必要があります。そのため、 絶対に技術的な理由はありません$
が置換文字列の後方参照のエスケープシンボルである理由。これは\
である可能性があります。 $
の代わりに\
を使用してエスケープすることもできますが、技術的には問題なく機能します。
理由Javaが正規表現を行う方法は、純粋に伝統的です:Perlによって設定された前例に従うだけです。