Vor Java 8 teilen wir uns auf leere Zeichenfolgen wie
String[] tokens = "abc".split("");
Der Aufteilungsmechanismus würde sich an Stellen aufteilen, die mit markiert sind |
|a|b|c|
weil ""
vor und nach jedem Zeichen ein leerer Raum existiert. Als Ergebnis würde es zunächst dieses Array erzeugen
["", "a", "b", "c", ""]
und später werden nachfolgende leere Zeichenfolgen entfernt (da wir dem limit
Argument keinen expliziten negativen Wert gegeben haben ), sodass es schließlich zurückkehrt
["", "a", "b", "c"]
In Java 8 scheint sich der Split-Mechanismus geändert zu haben. Nun, wenn wir verwenden
"abc".split("")
Wir werden ["a", "b", "c"]
stattdessen ein Array erhalten, ["", "a", "b", "c"]
so dass es so aussieht, als würden leere Zeichenfolgen beim Start ebenfalls entfernt. Aber diese Theorie scheitert zum Beispiel daran
"abc".split("a")
Gibt beim Start ein Array mit einer leeren Zeichenfolge zurück ["", "bc"]
.
Kann jemand erklären, was hier vor sich geht und wie sich die Split-Regeln in Java 8 geändert haben?
s.split("(?!^)")
scheint zu funktionieren.split("")
anstatt kryptischer (für Leute , die benutzen regex nicht)split("(?!^)")
odersplit("(?<!^)")
oder einige andere reguläre Ausdrücke.Antworten:
Das Verhalten von
String.split
(welches aufruftPattern.split
) ändert sich zwischen Java 7 und Java 8.Dokumentation
Beim Vergleich zwischen der Dokumentation
Pattern.split
in Java 7 und Java 8 wird die folgende Klausel hinzugefügt:Dieselbe Klausel wird auch
String.split
in Java 8 im Vergleich zu Java 7 hinzugefügt .Referenzimplementierung
Vergleichen wir den Code
Pattern.split
der Referenzimplemetation in Java 7 und Java 8. Der Code wird aus grepcode für die Versionen 7u40-b43 und 8-b132 abgerufen.Java 7
Java 8
Das Hinzufügen des folgenden Codes in Java 8 schließt die Übereinstimmung mit der Länge Null am Anfang der Eingabezeichenfolge aus, was das obige Verhalten erklärt.
Kompatibilität aufrechterhalten
Folgendes Verhalten in Java 8 und höher
So
split
verhalten Sie sich über Versionen hinweg konsistent und mit dem Verhalten in Java 8 kompatibel:(?!\A)
am Ende der Regex hinzu und verpacken Sie die ursprüngliche Regex in eine nicht erfassende Gruppe(?:...)
(falls erforderlich).(?!\A)
Überprüft, ob die Zeichenfolge nicht am Anfang der Zeichenfolge endet. Dies bedeutet, dass die Übereinstimmung am Anfang der Zeichenfolge eine leere Übereinstimmung ist.Folgendes Verhalten in Java 7 und früher
Es gibt keine allgemeine Lösung, um die
split
Abwärtskompatibilität mit Java 7 und früheren Versionen zu gewährleisten, ohne alle Instanzen vonsplit
zu ersetzen , um auf Ihre eigene benutzerdefinierte Implementierung zu verweisen.quelle
split("")
Code so ändern kann, dass er über verschiedene Java-Versionen hinweg konsistent ist?(?!^)
es am Ende des regulären Ausdrucks hinzufügen und den ursprünglichen regulären Ausdruck in eine nicht erfassende Gruppe einschließen(?:...)
(falls erforderlich), aber ich kann mir keine vorstellen Möglichkeit, es abwärtskompatibel zu machen (folgen Sie dem alten Verhalten in Java 7 und früheren Versionen)."(?!^)"
? In welchen Szenarien wird es anders sein""
? (Ich bin schrecklich bei Regex !: - /).Pattern.MULTILINE
Flag beeinflusst, während es\A
unabhängig von den Flags immer am Anfang der Zeichenfolge übereinstimmt.Dies wurde in der Dokumentation von angegeben
split(String regex, limit)
.In haben
"abc".split("")
Sie am Anfang eine Übereinstimmung mit der Breite Null erhalten, sodass der führende leere Teilstring nicht im resultierenden Array enthalten ist.In Ihrem zweiten Snippet haben Sie jedoch beim
"a"
Teilen eine positive Breitenübereinstimmung erhalten (in diesem Fall 1), sodass der leere führende Teilstring wie erwartet enthalten ist.(Irrelevanten Quellcode entfernt)
quelle
In den Dokumenten für
split()
Java 7 wurde eine geringfügige Änderung von Java 8 vorgenommen. Insbesondere wurde die folgende Anweisung hinzugefügt:(Hervorhebung von mir)
Die Aufteilung der leeren Zeichenfolge generiert zu Beginn eine Übereinstimmung mit der Breite Null, sodass am Anfang des resultierenden Arrays keine leere Zeichenfolge gemäß den obigen Angaben enthalten ist. Im Gegensatz dazu
"a"
generiert Ihr zweites Beispiel, das sich aufteilt, am Anfang der Zeichenfolge eine positive Breitenübereinstimmung, sodass am Anfang des resultierenden Arrays tatsächlich eine leere Zeichenfolge enthalten ist.quelle
"some-string".split("")
ein ziemlich seltener Fall ist..split("")
ist nicht die einzige Möglichkeit, sich zu teilen, ohne etwas zusammenzubringen. Wir haben einen positiven Lookahead-Regex verwendet, der in jdk7 ebenfalls am Anfang übereinstimmte und ein leeres Kopfelement erzeugte, das jetzt weg ist. github.com/spray/spray/commit/…