Warum verhält sich \R in regulären Ausdrücken zwischen Java 8 und Java 9 unterschiedlich?

Lesezeit: 4 Minuten

Benutzer-Avatar
German Bouzas

Der folgende Code lässt sich sowohl in Java 8 als auch in 9 kompilieren, verhält sich jedoch anders.

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);
        }
    }
}

Wenn ich es mit Java 8 ausführe, gibt es Folgendes zurück:

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

Aber wenn ich es mit Java 9 ausführe, ist die Ausgabe anders:

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

Wieso den?

  • Sieht aus wie in Java 8 \R ist gierig, während es in 9 nicht der Fall ist.

    Benutzer319799

    18. Dezember 2017 um 16:02 Uhr

  • Aus welcher Saite kommst du System.getProperty("line.separator")?

    – Sergej Kalinitschenko

    18. Dezember 2017 um 16:06 Uhr

  • @dasblinkenlight: Das sollte keine Rolle spielen; \R ist der Linebreak-Matcher. Es passt zu allem, was das OP dort hat.

    – Makoto

    18. Dezember 2017 um 16:08 Uhr

  • Wenn Sie diese Art von Frage stellen, lohnt es sich, die JDK-Versionsnummern anzugeben, da dies manchmal Fehler sind, die in Point-Releases behoben wurden, und die Leute dann nicht replizieren können usw.

    – Schlitten

    18. Dezember 2017 um 17:37 Uhr

  • @doublep Ich bin mir nicht sicher, ob Sie es als gierig bezeichnen würden, aber es ist nicht erlaubt, eine einzelne CR-LF-Sequenz beim Abgleich zurückzuverfolgen und in zwei Teile zu zerlegen \R, weil es verboten ist, nur ein CR zu finden, wenn LF folgt. Eine andere Möglichkeit, dies auszudrücken, ist, dass es nicht zurückgehen kann. Java 8 war korrekt; Soweit ich das beurteilen kann, ist Java 9 jetzt nicht mehr konform mit tr18.

    – tchrist

    19. Dezember 2017 um 2:32 Uhr


Benutzer-Avatar
Benutzer158037

Es war ein Fehler in Java 8 und er wurde behoben: JDK-8176029: „Der Zeilenumbruch-Matcher entspricht nicht dem in Javadoc angegebenen Muster“.

Siehe auch: Java-8 Regex negatives Lookbehind mit `\R`

  • Interessant, für mich sieht das Verhalten von Java 8 vernünftiger aus. Obwohl es möglich ist, “\r\n” als zwei aufeinanderfolgende Zeilenumbrüche zu interpretieren, macht es meines Erachtens wenig Sinn. Wenn Sie zwei Zeilenumbrüche meinten, würden Sie “\n\n” oder “\r\n\r\n” usw. schreiben, also zwei gleich Zeilenumbrüche. “\r\n” sollte eigentlich nur eine bedeuten.

    Benutzer319799

    18. Dezember 2017 um 16:14 Uhr

  • Es ergibt Sinn!. Aber Java 8 hatte das Verhalten, das ich brauchte. mmmh.

    – German Bouzas

    18. Dezember 2017 um 16:18 Uhr


  • @ GermánBouzas: Ich denke, Sie müssten zuerst Zeilenumbrüche normalisieren, zB mit replaceAll ("\\R", "\\n") (nicht getestet, aber ich würde vermuten, dass das Zurückverfolgen von Änderungen hier keine Rolle spielt).

    Benutzer319799

    18. Dezember 2017 um 16:26 Uhr

  • Ich bin mir ziemlich sicher, dass das ein Fehler ist. \R soll nicht rückverfolgbar sein; dafür gibt es handfeste gründe. Ich werde sehen, was ich finden kann: Sie dürfen ein CRLF niemals in zwei Instanzen aufteilen oder \R.

    – tchrist

    19. Dezember 2017 um 1:53 Uhr


Benutzer-Avatar
tchrist

Das Java-Dokumentation entspricht nicht dem Unicode-Standard. Das Javadoc verwechselt was \R soll passen. Es liest:

\R Jede Unicode-Zeilenumbruchsequenz ist äquivalent zu \u000D\u000A|[\u000A\u000B\u000C\u000D\u0085\u2028\u2029]

Diese Java-Dokumentation ist fehlerhaft. In seinem Abschnitt zu R1.6 Zeilenumbrüchen, Technischer Unicode-Standard Nr. 18 zu regulären Ausdrücken klar sagt:

Es wird dringend empfohlen, dass ein Metazeichen für reguläre Ausdrücke vorhanden ist, z. B. “\R”, um alle oben aufgeführten Zeilenendezeichen und Sequenzen (z. B. in #1) abzugleichen. Dies würde dem folgenden Ausdruck entsprechen. Dieser Ausdruck wird durch die Notwendigkeit, Backups zu vermeiden, etwas verkompliziert.

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

Mit anderen Worten, es kann nur mit einer CR+LF-Sequenz (Wagenrücklauf + Zeilenvorschub) mit zwei Codepunkten übereinstimmen oder aber ein einzelner Codepunkt aus diesem Satz, sofern dies der Fall ist nicht nur ein Wagenrücklauf allein, dem dann ein Zeilenvorschub folgt. Das ist, weil es ist darf nicht sichern. CRLF muss für atomar sein \R um richtig zu funktionieren.

Java 9 entspricht also nicht mehr dem, was R1.6 dringend empfiehlt. Darüber hinaus tut es jetzt etwas, was es in Java 8 NICHT tun sollte und nicht getan hat.

Sieht so aus, als wäre es Zeit für mich, Sherman (sprich: Xueming Shen) noch einmal zu brüllen. Ich habe schon früher mit ihm an diesen wesentlichen Fragen der formellen Konformität gearbeitet.

  • Eine Problemumgehung wäre also, entweder zu verwenden (?>\\R) oder \\R{1}+ Anstatt von \\Roder im speziellen Fall des OP verwenden \\R{2}+ Anstatt von \\R\\R. Interessanterweise sogar \\R{1}\\R{1} oder \\R{2} geben Sie das gewünschte Ergebnis unter Java 9, das inkonsistent ist, als nicht-possessiv an {n} sollte das Backtracking nicht deaktivieren.

    – Holger

    19. Dezember 2017 um 12:25 Uhr

  • Vielleicht lässt sich das mit beheben JDK-8176983?

    – Namann

    21. Dezember 2017 um 1:16 Uhr


  • @nullpointer kann mir jemand sagen, ob dies in Java 10 behoben wurde? Es sieht so aus, als hätte das Javadoc immer noch das falsche “äquivalente” Muster, also ist zumindest das Dokument falsch, wenn nicht die Implementierung.

    – Patrick Parker

    12. November 2018 um 2:55 Uhr

1205730cookie-checkWarum verhält sich \R in regulären Ausdrücken zwischen Java 8 und Java 9 unterschiedlich?

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy