Zachowanie String.split
(które wywołuje Pattern.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:
Jeśli na początku sekwencji wejściowej występuje dopasowanie o dodatniej szerokości, na początku otrzymanej tablicy zostanie umieszczony pusty podciąg wiodący. Jednak dopasowanie o zerowej szerokości na początku nigdy nie tworzy takiego pustego wiodącego podciągu.
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
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 dopasowanie o zerowej długości na początku ciągu wejściowego, co wyjaśnia powyższe zachowanie.
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
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:
- Jeśli twoje wyrażenie regularne może pasować do łańcucha o zerowej długości, po prostu dodaj
(?!\A)
na końcu wyrażenia regularnego i zawiń oryginalne wyrażenie regularne w grupę nieprzechwytywaną (?:...)
(jeśli to konieczne).
- Jeśli twoje wyrażenie regularne nie może dopasować ciągu o zerowej długości, nie musisz nic robić.
- Jeśli nie wiesz, czy wyrażenie regularne może pasować do ciągu o zerowej długości, czy nie, wykonaj obie czynności w kroku 1.
(?!\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ę.
s.split("(?!^)")
wydaje się działać.