web-dev-qa-db-ja.com

正規表現でJava 8とJava 9の間で\ Rが異なる動作をするのはなぜですか?

次のコードは、Java 8&9の両方でコンパイルされますが、動作が異なります。

class Simple {
    static String sample = "\nEn un lugar\r\nde la Mancha\nde cuyo nombre\r\nno quiero acordarme";

    public static void main(String args[]){
        String[] chunks = sample.split("\\R\\R");
        for (String chunk: chunks) {
            System.out.println("Chunk : "+chunk);
        }
    }
}

Java 8で実行すると、以下が返されます。

Chunk : 
En un lugar
de la Mancha
de cuyo nombre
no quiero acordarme

しかし、Java 9で実行すると、出力が異なります。

Chunk : 
En un lugar
Chunk : de la Mancha
de cuyo nombre
Chunk : no quiero acordarme

どうして?

77
Germán Bouzas

Javaドキュメント は、Unicode標準に準拠していません。 Javadocは、一致する\Rの内容を記載しています。それは読みます:

\R任意のUnicode改行シーケンスは、\u000D\u000A|[\u000A\u000B\u000C\u000D\u0085\u2028\u2029]と同等です

そのJavaドキュメンテーションはバグです。 R1.6改行のセクション、正規表現に関するUnicode技術標準#18 には次のように明記されています。

上記のすべての行末文字およびシーケンス(たとえば、#1)に一致するために、「\ R」などの正規表現メタ文字を使用することを強くお勧めします。これは、次の式に相当するものに対応します。 この式は、バックアップを避ける必要があるため、少し複雑です。

 (?:\u{D A}|(?!\u{D A})[\u{A}-\u{D}\u{85}\u{2028}\u{2029}]

つまり、2つのコードポイントCR + LF(キャリッジリターン+ラインフィード)シーケンスまたはelsenotだけで、その後に改行が続く場合、そのセットからの単一のコードポイント。それは、バックアップが許可されていないためです\Rが適切に機能するには、CRLFがアトミックである必要があります。

そのため、Java 9はR1.6が強く推奨するものに適合しなくなりました。さらに、Java 8。

シャーマン(Xueming Shenを読んでください)に再び大声で叫ぶ時が来たようです。私は以前、これらの正式な適合の重要な問題について彼と仕事をしたことがあります。

47
tchrist
63
user158037