2017-12-18 12 views
76

次のコードはJava 8 & 9でコンパイルされますが、動作は異なります。なぜ RはJava 8とJava 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 

なぜ?

+3

Java 8のように見える '\ R'は欲張りですが、9ではそうではありません。 – doublep

+0

'System.getProperty(" line.separator ")'からどんな文字列を取得しますか? – dasblinkenlight

+2

@dasblinkenlight:それは問題ではありません。 '\ R'は[改行マッチャー](https://docs.oracle.com/javase/9​​/docs/api/java/util/regex/Pattern.html)です。それはOPがそこにあるものと一致します。 – Makoto

答えて

46

Java documentationは、Unicode標準に準拠外です。 Javadocは\Rと一致するはずです。それは読む:

\RあらゆるUnicode改行シーケンスは、Javaドキュメントがバグだらけであることを\u000D\u000A|[\u000A\u000B\u000C\u000D\u0085\u2028\u2029]

に相当します。そのsection on R1.6 Line Breaks, Unicode Technical Standard #18 on Regular Expressionsに明記:

強く(例えば、#1で上に記載されている文字やシーケンスを終了するすべての行を一致させるためには、そのような「の\ R」として、正規表現のメタ文字があることが推奨されます)。これは、次の式に相当するものに相当します。 バックアップを避ける必要があるため、その表現はやや複雑です。

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

は、換言すれば、それだけあるいはそのセットから単一のコード・ポイントがそれことを条件とする2つのコード・ポイントCR + LF(キャリッジリターン+改行)配列を一致させることができではない単なる改行だけで改行が続きます。これは、がバックアップできないためです。です。 \Rが正しく機能するためには、CRLFはアトミックでなければなりません。

Java 9は、もはやR1.6が強く推奨するものに準拠しなくなりました。さらに、Java 8ではそれがやらないとやらないことをやろうとしていました。

シャーマン(Xueming Shenを読んでください)に再び挨拶をする時が来たようです。私は前もって彼と一緒に仕事をしてきました。

+1

回避策は、 '\\ R'の代わりに'(?> \\ R) 'または' \\ R {1} + 'を使うか、OPの特殊なケースで' \\ R {2 } \ 'R \\ R'の代わりに' \\ R '。興味深いことに、非所有的な '{n} 'はJava 9で矛盾しているので、' \\ R {1} \\ R {1} 'や' \\ R {2}バックトラッキングを無効にします。 – Holger

+0

これは[JDK-8176983](https://bugs.openjdk.java.net/browse/JDK-8176983)で修正される可能性がありますか? – nullpointer

63
+7

興味深い、私にはJava 8の行動は賢明に見えます。 "\ r \ n"を2つの連続した改行として解釈することは可能ですが、わかるようにほとんど意味がありません。 2つの改行を意味する場合は、「\ n \ n」または「\ r \ n \ r \ n」など、つまり2つの同じ*改行を書いてください。 "\ r \ n"は本当にただ一つを意味するはずです。 – doublep

+2

それは意味をなさない!しかし、Java 8は私が必要とする動作を持っていました。 mmmh。 –

+3

@GermánBouzas:最初に改行を正規化する必要があると思います。 'replaceAll(" \\ R "、" \\ n ")'(テストされていませんが、バックトラックの変更はここでは何の役割も果たしません)。 – doublep

関連する問題