Il comportamento di String.split
(che chiama Pattern.split
) cambia tra Java 7 e Java 8.
Documentazione
Confrontando tra la documentazione Pattern.split
in Java 7 e Java 8 , osserviamo la seguente clausola che si aggiunge:
Quando c'è una corrispondenza di larghezza positiva all'inizio della sequenza di input, viene inclusa una sottostringa iniziale vuota all'inizio della matrice risultante. Una corrispondenza di larghezza zero all'inizio tuttavia non produce mai una sottostringa iniziale così vuota.
La stessa clausola viene aggiunta anche String.split
in Java 8 , rispetto a Java 7 .
Implementazione di riferimento
Confrontiamo il codice dell'implementazione Pattern.split
di riferimento in Java 7 e Java 8. Il codice viene recuperato da grepcode, per le versioni 7u40-b43 e 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);
}
L'aggiunta del seguente codice in Java 8 esclude la corrispondenza di lunghezza zero all'inizio della stringa di input, il che spiega il comportamento precedente.
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;
}
Mantenere la compatibilità
Seguendo il comportamento in Java 8 e versioni successive
Per fare in split
modo che si comporti in modo coerente tra le versioni e compatibile con il comportamento in Java 8:
- Se la tua regex può corrispondere a una stringa di lunghezza zero, aggiungi semplicemente
(?!\A)
alla fine dell'espressione regolare e avvolgi la regex originale in un gruppo non di acquisizione (?:...)
(se necessario).
- Se la tua regex non può corrispondere a una stringa di lunghezza zero, non devi fare nulla.
- Se non sai se la regex può abbinare o meno una stringa di lunghezza zero, esegui entrambe le azioni nel passaggio 1.
(?!\A)
controlla che la stringa non finisca all'inizio della stringa, il che implica che la corrispondenza è una corrispondenza vuota all'inizio della stringa.
Seguendo il comportamento in Java 7 e precedenti
Non esiste una soluzione generale per rendere split
retrocompatibile con Java 7 e precedenti, a meno di sostituire tutte le istanze di split
per puntare alla propria implementazione personalizzata.
s.split("(?!^)")
sembra funzionare.