51 votes

Sortie confuse de String.split

Je ne comprends pas le résultat de ce code :

public class StringDemo{              
    public static void main(String args[]) {
        String blank = "";                    
        String comma = ",";                   
        System.out.println("Output1: "+blank.split(",").length);  
        System.out.println("Output2: "+comma.split(",").length);  
    }
}

Et j'ai obtenu le résultat suivant :

Output1: 1 
Output2: 0

54voto

Marco Acierno Points 6308

Documentation :

Pour : System.out.println("Output1: "+blank.split(",").length);

Le tableau renvoyé par cette méthode contient chaque sous-chaîne de cette chaîne qui est terminée par une autre sous-chaîne correspondant à l'expression donnée ou qui est terminée par la fin de la chaîne. Les sous-chaînes dans le tableau sont dans l'ordre dans lequel elles apparaissent dans cette chaîne. Si l'expression ne correspond à aucune partie de l'entrée, le tableau résultant ne comporte qu'un seul élément, à savoir cette chaîne de caractères .

Il renvoie simplement la chaîne entière, c'est pourquoi il renvoie 1.


Pour le deuxième cas, String.split rejettera le , donc le résultat sera vide.

String.split silently discards trailing separators

voir guava StringsExplained trop

34voto

Pshemo Points 34648

Tout se passe comme prévu, mais procédons étape par étape (j'espère que vous avez du temps).

Selon documentation (et code source ) de split(String regex) méthode) :

Cette méthode fonctionne comme si elle invoquait la méthode de division à deux arguments avec l'expression donnée et un argument limite de zéro.

Ainsi, lorsque vous invoquez

split(String regex)

vous obtenez réellement le résultat de la split(String regex, int limit) qui est invoquée d'une certaine manière :

split(regex, 0)

Alors ici limit est réglé sur 0 .

Vous devez savoir certaines choses sur ce paramètre :

  • Si limit est positif, vous limitez la longueur du tableau de résultats à un nombre positif que vous avez spécifié, donc "axaxaxaxa".split("x",2) retournera un tableau, ["a", "axaxaxa"] pas ["a","a","a","a","a"] .
  • Si limit est 0 alors vous ne limitez pas la longueur du tableau de résultats. Mais cela signifie également que toutes les chaînes vides de fin de tableau seront supprimées. Par exemple :

    "fooXbarX".split("X")

générera au départ un tableau qui ressemblera à ceci :

    ["foo", "bar", ""]

( "barX" divisé sur "X" génère "bar" y "" ), mais comme split supprime toutes les chaînes vides à la fin, il retournera

    ["foo", "bar"]
  • Comportement de la valeur négative de limit est similaire à un comportement où la limite est fixée à 0 (il ne limitera pas la longueur du tableau de résultats). La seule différence est qu'elle ne supprimera pas les chaînes vides à la fin du tableau de résultats. En d'autres termes

    "fooXbarX".split("X",-1)

retournera ["foo", "bar", ""]


Je vais essayer de répondre pour le premier cas,

",".split(",").length

qui, comme expliqué précédemment, est effectivement identique à

",".split(",", 0).length

Cela signifie que nous utilisons une version de split qui ne limite pas la longueur du tableau de résultats, MAIS qui supprime toutes les chaînes vides à la fin, "" . Vous devez comprendre que lorsque nous nous séparons un chose que nous obtenons toujours deux des choses.

En d'autres termes, si nous divisons "abc" à la place de b nous obtiendrons "a" y "c" .
La partie délicate est de comprendre que si nous divisons "abc" sur c nous obtiendrons "ab" and ""` (chaîne vide).

En utilisant cette logique, si nous divisons "," en , nous obtiendrons "" y "" (deux chaînes de caractères vides).

Vous pouvez le vérifier facilement en utilisant

for (String s: ",".split(",", -1))
    System.out.println("\""+s+"\"");

qui produira

""
""

Notre tableau de résultats contient donc ["", ""] .

Mais nous avons mis limit a 0 Nous avons donc décidé que toutes les chaînes vides seront supprimées. Dans ce cas, le tableau de résultats contient uniquement les chaînes vides à la fin Así que ils seront tous supprimés vous laissant avec un tableau vide [] dont la longueur est 0 .


Pour répondre au cas avec

"".split(",").length

vous devez comprendre que la suppression des chaînes vides de fin n'a de sens que si ces chaînes vides de fin sont le résultat d'un fractionnement (et sont très probablement indésirables) . Donc, s'il n'y avait pas d'endroits sur lesquels nous pourrions nous diviser, il n'y aurait pas de raison de lancer ce processus de "nettoyage". C'est pourquoi, dans le cas où la chaîne de caractères d'origine n'a pas été séparée (elle ne contenait pas de partie correspondant à une regex de l'option split Les créateurs de Java ont décidé de retourner cette chaîne telle quelle.

Cette information est mentionnée dans la documentation de split(String regex, int limit) où vous pouvez lire :

Si l'expression ne correspond à aucune partie de l'entrée alors le Le tableau résultant n'a qu'un seul élément, à savoir cette chaîne de caractères .

Vous pouvez également observer ce comportement dans code source de cette méthode (à partir de Java 8) :

2316 public String[] split(String regex, int limit) {
2317 /* fastpath if the regex is a
2318 (1)one-char String and this character is not one of the
2319 RegEx's meta characters ".$|()[{^?*+\\", or
2320 (2)two-char String and the first char is the backslash and
2321 the second is not the ascii digit or ascii letter.
2322 */
2323 char ch = 0;
2324 if (((regex.value.length == 1 &&
2325 ".$|()[{^?*+\\".indexOf(ch = regex.charAt(0)) == -1) ||
2326 (regex.length() == 2 &&
2327 regex.charAt(0) == '\\' &&
2328 (((ch = regex.charAt(1))-'0')|('9'-ch)) < 0 &&
2329 ((ch-'a')|('z'-ch)) < 0 &&
2330 ((ch-'A')|('Z'-ch)) < 0)) &&
2331 (ch < Character.MIN_HIGH_SURROGATE ||
2332 ch > Character.MAX_LOW_SURROGATE))
2333 {
2334 int off = 0;
2335 int next = 0;
2336 boolean limited = limit > 0;
2337 ArrayList<String> list = new ArrayList<>();
2338 while ((next = indexOf(ch, off)) != -1) {
2339 if (!limited || list.size() < limit - 1) {
2340 list.add(substring(off, next));
2341 off = next + 1;
2342 } else { // last one
2343 //assert (list.size() == limit - 1);
2344 list.add(substring(off, value.length));
2345 off = value.length;
2346 break;
2347 }
2348 }
2349 // If no match was found, return this
2350 if (off == 0)
2351 return new String[]{this};
2353 // Add remaining segment
2354 if (!limited || list.size() < limit)
2355 list.add(substring(off, value.length));
2357 // Construct result
2358 int resultSize = list.size();
2359 if (limit == 0) {
2360 while (resultSize > 0 && list.get(resultSize - 1).length() == 0) {
2361 resultSize--;
2362 }
2363 }
2364 String[] result = new String[resultSize];
2365 return list.subList(0, resultSize).toArray(result);
2366 }
2367 return Pattern.compile(regex).split(this, limit);
2368 }

où vous pouvez trouver

if (off == 0)
    return new String[]{this};

qui signifie

  • if (off == 0) - si off (position à partir de laquelle la méthode doit commencer à chercher la prochaine correspondance possible pour l'expression rationnelle passée en tant que split argument) est toujours 0 après avoir itéré sur la chaîne entière, cela signifie que la méthode n'a pas trouvé de correspondance, donc la chaîne n'a pas été divisée.
  • return new String[]{this}; dans ce cas, nous retournons simplement un tableau avec les données originales/actuelles ( this ) chaîne.

Desde "," n'a pas pu être trouvé dans "" même une fois, "".split(",") doit retourner un tableau avec un élément (chaîne vide sur laquelle vous avez invoqué split ). Cela signifie que la longueur de ce tableau est 1 .

7voto

Naveen Points 4316

De la Documentation sur Java 1.7

Divise la chaîne de caractères autour des correspondances de l'expression régulière donnée.

La méthode split() fonctionne comme si elle invoquait la méthode split à deux arguments avec l'expression donnée et un argument limite de zéro. Les chaînes vides ne sont donc pas incluses dans le tableau résultant.

Dans le cas 1 blank.split(",") does not match any part of the input then the resulting array has just one element, namely this String.

It will return entire String. Donc, la longueur sera 1 .

Dans le cas 2 comma.split(",") will return empty.

split() attend une expression rationnelle comme argument, renvoie un tableau de résultats correspondant à cette expression rationnelle.

Donc, la longueur est 0

Par exemple ( Documentation )

La chaîne "boo:et:foo" donne les résultats suivants avec ces expressions :

Regex     Result
  :     { "boo", "and", "foo" }
  o     { "b", "", ":and:f" }

Paramètres : regex - l'expression régulière de délimitation

Les retours : le tableau de chaînes calculé en divisant cette chaîne autour des correspondances de l'expression régulière donnée

Les lancers : PatternSyntaxException - si la syntaxe de l'expression régulière n'est pas valide.

4voto

ivan_nikolaev Points 142

De la classe String javadoc pour le public String[] **split**(String regex) méthode :

Divise cette chaîne de caractères en fonction des correspondances de l'expression régulière donnée.

Cette méthode fonctionne comme si elle invoquait la méthode de division à deux arguments avec l'expression donnée et un argument limite de zéro. Les chaînes vides ne sont donc pas incluses dans le tableau résultant.

Dans le premier cas, l'expression ne correspond à aucune partie de l'entrée, nous avons donc obtenu un tableau avec un seul élément - l'entrée.

Dans le second cas, l'expression correspond à l'entrée et split devrait retourner deux chaînes vides ; mais, selon la javadoc, elles sont écartées (parce qu'elles sont vides et traînantes).

3voto

Tichodroma Points 11266

Nous pouvons jeter un coup d'œil sur le code source de java.util.regex.Pattern qui se trouve derrière String.split . Au fond du terrier du lapin, la méthode

public String[] split(CharSequence input, int limit)

est invoquée.

Entrée ""

Pour l'entrée "" cette méthode est appelée comme

String[] parts = split("", 0);

La partie intéressante de cette méthode est :

  int index = 0;
  boolean matchLimited = limit > 0;
  ArrayList<String> matchList = new ArrayList<>();
  Matcher m = matcher(input);

  while(m.find()) {
    // Tichodroma: this will not happen for our input
  }

  // If no match was found, return this
  if (index == 0)
    return new String[] {input.toString()};

Et c'est ce qui se passe : new String[] {input.toString()} est retourné .

Entrée ","

Pour l'entrée "," la partie intéressante est

    // 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);

Ici resultSize == 0 y limit == 0 así que new String[0] est retourné .

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X