Dlaczego w Javie 8 split czasami usuwa puste ciągi na początku tablicy wyników?
Przed Java 8 kiedy dzielimy na pusty łańcuch jak
String[] tokens = "abc".split("");
Mechanizm podziału podzieliłby się w miejscach oznaczonych |
|a|b|c|
Ponieważ pusta przestrzeń ""
istnieje przed i po każdym znaku. Tak więc jako wynik generowałby on najpierw tą tablicę
["", "a", "b", "c", ""]
I później usunie końcowe puste ciągi (ponieważ nie podaliśmy jawnie ujemnej wartości argumentu limit
), więc w końcu zwróci
["", "a", "b", "c"]
W Wydaje się, że mechanizm podziału Java 8 uległ zmianie. Teraz kiedy używamy
"abc".split("")
Otrzymamy ["a", "b", "c"]
tablicę zamiast ["", "a", "b", "c"]
, więc wygląda na to, że puste ciągi na początku są również usuwane. Ale ta teoria zawodzi, ponieważ na przykład
"abc".split("a")
Zwraca tablicę z pustym łańcuchem na początku ["", "bc"]
.
Czy ktoś może wyjaśnić o co tu chodzi i jak zmieniły się zasady podziału w tym przypadku w Javie 8?
3 answers
Zachowanie String.split
(wywołujące Pattern.split
) zmienia się pomiędzy Javą 7 a Javą 8.
Dokumentacja
Porównując dokumentację Pattern.split
w Java 7 i Java 8 , obserwujemy dodawanie następującej klauzuli:
Jeżeli na początku sekwencji wejściowej występuje dopasowanie o dodatniej szerokości, to na początku tablicy wynikowej jest dołączony pusty podłańcuch wiodący. Mecz o zerową szerokość na początku jednak nigdy wytworzy taki pusty podciąg wiodący.
Ta sama klauzula jest również dodana do String.split
w Java 8, w porównaniu do Java 7 .
Implementacja odniesienia
Porównajmy kod Pattern.split
implementacji odniesienia w Javie 7 i Javie 8. Kod jest pobierany z grepcode, dla wersji 7u40-b43 i 8-b132.
Java 7
public String[] split(CharSequence input, int limit) {
int index = 0;
boolean matchLimited = limit > 0;
ArrayList<String> matchList = new ArrayList<>();
Matcher m = matcher(input);
// Add segments before each match found
while(m.find()) {
if (!matchLimited || matchList.size() < limit - 1) {
String match = input.subSequence(index, m.start()).toString();
matchList.add(match);
index = m.end();
} else if (matchList.size() == limit - 1) { // last one
String match = input.subSequence(index,
input.length()).toString();
matchList.add(match);
index = m.end();
}
}
// If no match was found, return this
if (index == 0)
return new String[] {input.toString()};
// Add remaining segment
if (!matchLimited || matchList.size() < limit)
matchList.add(input.subSequence(index, input.length()).toString());
// Construct result
int resultSize = matchList.size();
if (limit == 0)
while (resultSize > 0 && matchList.get(resultSize-1).equals(""))
resultSize--;
String[] result = new String[resultSize];
return matchList.subList(0, resultSize).toArray(result);
}
Java 8
public String[] split(CharSequence input, int limit) {
int index = 0;
boolean matchLimited = limit > 0;
ArrayList<String> matchList = new ArrayList<>();
Matcher m = matcher(input);
// Add segments before each match found
while(m.find()) {
if (!matchLimited || matchList.size() < limit - 1) {
if (index == 0 && index == m.start() && m.start() == m.end()) {
// no empty leading substring included for zero-width match
// at the beginning of the input char sequence.
continue;
}
String match = input.subSequence(index, m.start()).toString();
matchList.add(match);
index = m.end();
} else if (matchList.size() == limit - 1) { // last one
String match = input.subSequence(index,
input.length()).toString();
matchList.add(match);
index = m.end();
}
}
// If no match was found, return this
if (index == 0)
return new String[] {input.toString()};
// Add remaining segment
if (!matchLimited || matchList.size() < limit)
matchList.add(input.subSequence(index, input.length()).toString());
// Construct result
int resultSize = matchList.size();
if (limit == 0)
while (resultSize > 0 && matchList.get(resultSize-1).equals(""))
resultSize--;
String[] result = new String[resultSize];
return matchList.subList(0, resultSize).toArray(result);
}
Dodanie następującego kodu w Javie 8 wyklucza zerową długość dopasuj na początku łańcucha wejściowego, co wyjaśnia zachowanie powyżej.
if (index == 0 && index == m.start() && m.start() == m.end()) {
// no empty leading substring included for zero-width match
// at the beginning of the input char sequence.
continue;
}
Utrzymanie zgodności
[[38]}następujące zachowanie w Javie 8 i nowszych [39]}Aby split
zachowywał się konsekwentnie we wszystkich wersjach i był zgodny z zachowaniem w Javie 8:
- jeśli Twoje regex może dopasować łańcuch o zerowej długości, po prostu dodaj
(?!\A)
na koniec Z regex i zawiń oryginalne regex w grupie nie przechwytywającej(?:...)
(jeśli konieczne). - jeśli twój regex nie może dopasować łańcucha o zerowej długości, nie musisz nic robić.
- jeśli nie wiesz, czy regex może dopasować łańcuch o zerowej długości, wykonaj obie akcje w kroku 1.
(?!\A)
sprawdza, czy łańcuch nie kończy się na początku łańcucha, co oznacza, że dopasowanie jest puste na początku łańcucha.
Nie ma ogólnego rozwiązania upewnij się, że split
jest wstecznie kompatybilny z Java 7 i wcześniejszymi, bez zastąpienia wszystkich instancji split
, aby wskazać własną, niestandardową implementację.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-10-30 04:13:54
Zostało to określone w dokumentacji split(String regex, limit)
.
Gdy na początku tego ciągu występuje dopasowanie dodatniej szerokości wtedy na początku jest dołączony pusty podłańcuch wiodący wynikowa tablica. Mecz o zerową szerokość na początku jednak nigdy wytworzy taki pusty podciąg wiodący.
W "abc".split("")
masz dopasowanie o zerowej szerokości na początku, więc wiodący pusty podłańcuch nie jest zawarty w wynikowej tablicy.
Jednak w drugim fragmencie po rozdzieleniu na "a"
otrzymujesz dodatnie dopasowanie szerokości (w tym przypadku 1), więc pusty podciąg wiodący jest uwzględniany zgodnie z oczekiwaniami.
(usunięto nieistotny kod źródłowy)
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-02-19 16:28:19
Nastąpiła drobna zmiana w dokumentach dla split()
od Javy 7 do Javy 8. W szczególności dodano następujące stwierdzenie:
Jeżeli na początku tego łańcucha jest dopasowanie o dodatniej szerokości, to na początku tablicy wynikowej jest dołączony pusty podłańcuch wiodący. dopasowanie o zerowej szerokości na początku nigdy nie tworzy takiego pustego podcięcia.
(moje)
Pusty podział łańcuchów generuje dopasowanie o zerowej szerokości na początku, więc pusty łańcuch nie jest dołączany na początku wynikowej tablicy zgodnie z powyższym opisem. Dla kontrastu, twój drugi przykład, który dzieli się na "a"
generuje dodatnie dopasowanie szerokości na początku łańcucha, więc pusty łańcuch jest w rzeczywistości zawarty na początku tablicy wynikowej.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-03-28 16:57:43