Avant Java 8 lorsque nous nous sommes séparés sur une chaîne vide comme
String[] tokens = "abc".split("");
le mécanisme de scission se scinde aux endroits marqués avec |
|a|b|c|
car un espace vide ""
existe avant et après chaque caractère. Donc, comme résultat, il générerait d'abord ce tableau
["", "a", "b", "c", ""]
et plus tard, supprimera les chaînes vides de fin (car nous n’avons pas explicitement fourni de valeur négative à limit
argument) afin qu’il retourne finalement
["", "a", "b", "c"]
Dans Java 8 le mécanisme de division semble avoir changé. Maintenant, lorsque nous utilisons
"abc".split("")
nous aurons le tableau ["a", "b", "c"]
au lieu de ["", "a", "b", "c"]
, de sorte qu'il semble que les chaînes vides au début sont également supprimées. Mais cette théorie échoue parce que par exemple
"abc".split("a")
retourne un tableau avec une chaîne vide au début ["", "bc"]
.
Quelqu'un peut-il expliquer ce qui se passe ici et comment les règles de scission ont changé dans Java 8?
Le comportement de String.split
(qui appelle Pattern.split
) change entre Java 7 et Java 8.
Comparaison entre la documentation de Pattern.split
in Java 7 et Java 8 , nous observons la clause suivante étant ajoutée:
Lorsqu'il y a une correspondance positive en largeur au début de la séquence d'entrée, une sous-chaîne principale vide est incluse au début du tableau résultant. Une correspondance de largeur zéro au début ne produit toutefois jamais une telle chaîne vide.
La même clause est également ajoutée à String.split
in Java 8 , comparé à Java 7 .
Comparons le code de Pattern.split
de l'implémentation de référence dans Java 7 et Java 8.). Le code est extrait de grepcode, pour les versions 7u40-b43 et 8-b132.
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);
}
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'ajout du code suivant dans Java 8 exclut la correspondance de longueur nulle au début de la chaîne d'entrée, ce qui explique le comportement ci-dessus.
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;
}
Pour que split
se comporte de manière cohérente d’une version à l’autre et compatible avec le comportement décrit dans Java 8:
(?!\A)
à la fin de la regex et envelopper la regex originale dans un groupe sans capture (?:...)
(si nécessaire).(?!\A)
vérifie que la chaîne ne se termine pas au début de la chaîne, ce qui implique que la correspondance est une correspondance vide au début de la chaîne.
Il n'y a pas de solution générale pour rendre split
rétrocompatible avec Java 7 et versions antérieures, à moins de remplacer toute instance de split
pour qu'il pointe vers votre propre implémentation personnalisée. .
Cela a été spécifié dans la documentation de split(String regex, limit)
.
Quand il y a une correspondance positive en largeur au début de cette chaîne, une sous-chaîne principale vide est incluse au début du tableau résultant. Une correspondance de largeur zéro au début ne produit toutefois jamais une telle chaîne vide.
Dans "abc".split("")
, vous obtenez une correspondance de largeur zéro au début, de sorte que la sous-chaîne vide en tête n'est pas incluse dans le tableau résultant.
Cependant, dans votre deuxième extrait lorsque vous vous séparez sur "a"
vous avez obtenu une correspondance de largeur positive (1 dans ce cas), de sorte que la sous-chaîne principale vide est incluse comme prévu.
(Code source non pertinent supprimé)
Il y a eu un léger changement dans la documentation pour split()
de Java 7 à Java 8.). la déclaration suivante a été ajoutée:
Quand il y a une correspondance positive en largeur au début de cette chaîne, une sous-chaîne principale vide est incluse au début du tableau résultant. Une correspondance de largeur zéro au début mais ne produit jamais une telle sous-chaîne principale vide.
(c'est moi qui souligne)
La division de chaîne vide génère une correspondance de largeur zéro au début. Par conséquent, une chaîne vide n'est pas incluse au début du tableau résultant, conformément à ce qui est spécifié ci-dessus. En revanche, votre deuxième exemple qui se divise sur "a"
génère une correspondance positive - width au début de la chaîne, de sorte qu'une chaîne vide est en fait incluse au début du tableau résultant.