Przed Java 8, kiedy dzielimy się na pusty ciąg, taki jak
String[] tokens = "abc".split("");
mechanizm split rozszczepiłby się w miejscach oznaczonych |
|a|b|c|
ponieważ pusta przestrzeń ""
istnieje przed i po każdym znaku. W rezultacie wygeneruje najpierw tę tablicę
["", "a", "b", "c", ""]
a później usunie końcowe puste ciągi (ponieważ nie podaliśmy jawnie wartości ujemnej dla limit
argumentu), więc w końcu zwróci
["", "a", "b", "c"]
Wydaje się, że mechanizm podziału Java 8 uległ zmianie. Teraz, kiedy używamy
"abc".split("")
["a", "b", "c"]
zamiast tego otrzymamy tablicę, ["", "a", "b", "c"]
więc wygląda na to, że puste ciągi znaków na początku są również usuwane. Ale ta teoria zawodzi, bo na przykład
"abc".split("a")
zwraca tablicę z pustym ciągiem na początku ["", "bc"]
.
Czy ktoś może wyjaśnić, co się tutaj dzieje i jak zmieniły się zasady podziału w Javie 8?
s.split("(?!^)")
wydaje się działać.split("")
zamiast tajemnicze (dla osób, które nie korzystają z regex)split("(?!^)")
lubsplit("(?<!^)")
lub kilku innych regexes.Odpowiedzi:
Zachowanie
String.split
(które wywołujePattern.split
) zmienia się między Java 7 i Java 8.Dokumentacja
Porównując dokumentację
Pattern.split
w językach Java 7 i Java 8 , zauważamy dodanie następującej klauzuli:Ta sama klauzula została również dodana
String.split
w Javie 8 w porównaniu z Javą 7 .Realizacja referencyjna
Porównajmy kod
Pattern.split
implementacji referencyjnej w Javie 7 i Java 8. Kod jest pobierany z grepcode dla wersji 7u40-b43 i 8-b132.Java 7
Java 8
Dodanie następującego kodu w Javie 8 wyklucza dopasowanie o zerowej długości na początku ciągu wejściowego, co wyjaśnia powyższe zachowanie.
Utrzymanie zgodności
Następujące zachowanie w Javie 8 i nowszych
Aby zapewnić
split
spójne zachowanie w różnych wersjach i zgodność z zachowaniem w języku Java 8:(?!\A)
na końcu wyrażenia regularnego i zawiń oryginalne wyrażenie regularne w grupę nieprzechwytywaną(?:...)
(jeśli to konieczne).(?!\A)
sprawdza, czy ciąg nie kończy się na początku ciągu, co oznacza, że dopasowanie jest pustym dopasowaniem na początku ciągu.Następujące zachowanie w Javie 7 i wcześniejszych
Nie ma ogólnego rozwiązania zapewniającego
split
zgodność wsteczną z wersją Java 7 i wcześniejszą, poza zastąpieniem wszystkich wystąpień,split
aby wskazywały na własną niestandardową implementację.źródło
split("")
kod, aby był spójny w różnych wersjach Java?(?!^)
do końca tego regex i zawinąć oryginalnego regex w niewyspecjalizowanych przechwytywanie grupy(?:...)
(jeśli to konieczne), ale nie mogę myśleć o każdym sposób, aby uczynić go kompatybilnym wstecz (postępuj zgodnie ze starym zachowaniem w Javie 7 i wcześniejszych)."(?!^)"
? W jakich scenariuszach będzie się to różnić""
? (Jestem okropny w regex!: - /).Pattern.MULTILINE
flaga, podczas gdy\A
zawsze pasuje na początku ciągu, niezależnie od flag.Zostało to określone w dokumentacji
split(String regex, limit)
.W
"abc".split("")
masz na początku dopasowanie o zerowej szerokości, więc wiodący pusty podciąg nie jest zawarty w wynikowej tablicy.Jednak w drugim fragmencie po podzieleniu
"a"
otrzymałeś dodatnie dopasowanie szerokości (w tym przypadku 1), więc pusty wiodący podciąg jest uwzględniony zgodnie z oczekiwaniami.(Usunięto nieistotny kod źródłowy)
źródło
Nastąpiła niewielka zmiana w dokumentacji
split()
z języka Java 7 do języka Java 8. W szczególności dodano następujące oświadczenie:(podkreślenie moje)
Podział na pusty ciąg generuje dopasowanie o zerowej szerokości na początku, więc pusty ciąg nie jest dołączany na początku wynikowej tablicy, zgodnie z tym, co określono powyżej. W przeciwieństwie do tego, twój drugi przykład, który dzieli się na,
"a"
generuje dopasowanie dodatniej szerokości na początku ciągu, więc pusty ciąg jest w rzeczywistości dołączany na początku wynikowej tablicy.źródło
"some-string".split("")
jest to dość rzadki przypadek..split("")
to nie jedyny sposób na dzielenie bez dopasowywania czegokolwiek. Użyliśmy pozytywnego wyrażenia regularnego lookahead, które w jdk7 również pasowało na początku i tworzyło pusty element head, którego teraz nie ma. github.com/spray/spray/commit/…