Pourquoi «fractionner» sur une chaîne vide renvoie-t-il un tableau non vide?

111

Split sur une chaîne vide renvoie un tableau de taille 1:

scala> "".split(',')
res1: Array[String] = Array("")

Considérez que cela renvoie un tableau vide:

scala> ",,,,".split(',')
res2: Array[String] = Array()

S'il vous plaît, expliquez :)

oluies
la source
5
En outre, cela semble incompatible avec le comportement observé lorsque la chaîne ne contient qu'une seule instance du séparateur. Dans ce cas, le résultat est en fait un tableau vide: ",". Split (","). Length == 0
LD.

Réponses:

37

Pour la même raison que

",test" split ','

et

",test," split ','

renverra un tableau de taille 2. Tout ce qui se trouve avant la première correspondance est renvoyé comme premier élément.

Daniel C. Sobral
la source
5
Une chaîne vide est une chaîne, pas rien. (n'importe où sauf dans Excel)
Raphael
5
@Raphael Or dans une base de données Oracle
Austin
7
@Raphael, dans n'importe quel autre langage de programmation "".split("wtf").lengthrenvoie 0. Seulement dans JS c'est 1.: /
Andrey Mikhaylov - lolmaus
11
@ DanielC.Sobral Ok, alors pourquoi "," split ","renvoie un tableau de 0?
Joan
5
Pourquoi tout n'est-il pas revenu après le dernier match?
Didier A.
72

Si vous divisez une orange zéro fois, vous avez exactement un morceau - l'orange.

Sam Stainsby
la source
8
Mais l'orange n'est pas vide (idk si c'est ce que signifiait oluies), c'est une orange. Peut-être fendre une orange qui devrait être là, mais qui ne l'est pas, donc vous récupérez une seule valeur: un espace vide xD
Nick Rolando
8
C'est une conversation profonde.
31
Cette métaphore a du sens pour "orange".split(','), mais n'est évidemment pas pertinente pour le fractionnement de chaînes vides. Si je divise mon manque d'orange zéro fois, je n'ai toujours pas d'orange; représentons-nous cela comme une liste vide de non-oranges, une liste d'exactement un non-orange, une liste de douze sans-oranges, ou quoi? Ce n'est pas une question de savoir avec quoi nous aboutissons, mais comment nous le représentons.
Matchu
1
Mais si vous divisez un livre inexistant par ses pages, vous n'obtiendrez rien.
SMUsamaShah
49

Les méthodes de fractionnement Java et Scala fonctionnent en deux étapes comme ceci:

  • Commencez par diviser la chaîne par un délimiteur. La conséquence naturelle est que si la chaîne ne contient pas le délimiteur, un tableau singleton contenant uniquement la chaîne d'entrée est renvoyé,
  • Deuxièmement, supprimez toutes les chaînes vides les plus à droite. C'est la raison pour laquelle ",,,".split(",")renvoie un tableau vide.

Selon cela, le résultat de "".split(",")devrait être un tableau vide à cause de la deuxième étape, non?

Cela devrait. Malheureusement, il s'agit d'un boîtier d'angle introduit artificiellement. Et c'est mauvais, mais au moins c'est documenté dans java.util.regex.Pattern, si vous vous souvenez de jeter un œil à la documentation:

Pour n == 0, le résultat est comme pour n <0, sauf que les chaînes vides de fin ne seront pas renvoyées. (Notez que le cas où l'entrée est elle-même une chaîne vide est spécial, comme décrit ci-dessus, et le paramètre limit ne s'applique pas là.)

Solution 1: passez toujours -1 comme deuxième paramètre

Donc, je vous conseille de toujours passer n == -1comme deuxième paramètre (cela sautera l'étape deux ci-dessus), à moins que vous ne sachiez spécifiquement ce que vous voulez réaliser / vous êtes sûr que la chaîne vide n'est pas quelque chose que votre programme obtiendrait en entrée.

Solution 2: utilisez la classe Guava Splitter

Si vous utilisez déjà Guava dans votre projet, vous pouvez essayer la classe Splitter (documentation) . Il possède une API très riche et rend votre code très facile à comprendre.

Splitter.on(".").split(".a.b.c.") // "", "a", "b", "c", ""
Splitter.on(",").omitEmptyStrings().split("a,,b,,c") // "a", "b", "c"
Splitter.on(CharMatcher.anyOf(",.")).split("a,b.c") // "a", "b", "c"
Splitter.onPattern("=>?").split("a=b=>c") // "a", "b", "c"
Splitter.on(",").limit(2).split("a,b,c") // "a", "b,c"
Rok Kralj
la source
1
+1, c'est la seule réponse qui cite effectivement la documentation et souligne qu'elle est incohérente. Cependant, je n'ai pas trouvé la partie en surbrillance du commentaire dans mon JavaDoc.
Yogu
Je l'ai trouvé dans java.util.regex.Pattern, mais il semble en grande partie disparu. Au moment de la rédaction de cet article, il était définitivement présent dans l'arborescence des sources officielle d'OpenJDK en tant que javadoc. android.googlesource.com/platform/libcore/+/… Peut-être devrions-nous signaler un bug?
Rok Kralj
Ce serait une bonne idée de signaler un bogue - le comportement ne sera certainement pas changé, mais il devrait au moins être documenté.
Yogu
@RokKralj Android n'a pas utilisé la bibliothèque OpenJDK, mais était plutôt basé sur Apache Harmony, alors peut-être que vous cherchez au mauvais endroit?
lxgr le
1
"".split (",", n)génère un tableau à un élément pour n dans (-1, 0, 1) avec Oracle JDK 8. Ce serait bien d'obtenir une liste de jetons non vides uniquement - supposons qu'une regex complète peut être nécessaire (quelque chose comme "[^,\\s]+[^,]*[^,\\s]*").
simon.watts
40

Le fractionnement d'une chaîne vide renvoie la chaîne vide comme premier élément. Si aucun délimiteur n'est trouvé dans la chaîne cible, vous obtiendrez un tableau de taille 1 qui contient la chaîne d'origine, même si elle est vide.

Nick Rolando
la source
2
Faux. Split supprime toutes les chaînes vides les plus à droite, le résultat doit donc être un tableau vide. Voyez ma réponse. ",".split(",")renvoie un tableau vide.
Rok Kralj
23

"a".split(",")-> "a" donc "".split(",")->""

weberjn
la source
6
Faux. Split supprime toutes les chaînes vides les plus à droite, le résultat doit donc être un tableau vide. Voyez ma réponse. ",".split(",")renvoie un tableau vide.
Rok Kralj
5

Dans tous les langages de programmation, je sais qu'une chaîne vide est toujours une chaîne valide. Donc, faire un fractionnement en utilisant n'importe quel délimiteur retournera toujours un tableau d'élément unique où cet élément est la chaîne vide. S'il s'agissait d'une chaîne nulle (et non vide), ce serait un problème différent.

brent777
la source
Je pense que c'est une fonction de bibliothèque et non une partie du langage. Par exemple, dans google guava, vous pouvez omettre les chaînes vides. > Morceaux <String> itérables = com.google.common.base.Splitter.on (','). OmitEmptyStrings (). Split ("");
oluies
2

Ce splitcomportement est hérité de Java, pour le meilleur ou pour le pire ...
Scala ne remplace pas la définition de la Stringprimitive.

Notez que vous pouvez utiliser l' limitargument pour modifier le comportement :

Le paramètre limit contrôle le nombre de fois où le motif est appliqué et affecte donc la longueur du tableau résultant. Si la limite n est supérieure à zéro, le modèle sera appliqué au plus n - 1 fois, la longueur du tableau ne sera pas supérieure à n et la dernière entrée du tableau contiendra toutes les entrées au-delà du dernier délimiteur correspondant. Si n n'est pas positif, le motif sera appliqué autant de fois que possible et le tableau peut avoir n'importe quelle longueur. Si n est égal à zéro, le motif sera appliqué autant de fois que possible, le tableau peut avoir n'importe quelle longueur et les chaînes vides de fin seront supprimées.

c'est-à-dire que vous pouvez définir le limit=-1pour obtenir le comportement de (tous?) autres langages:

@ ",a,,b,,".split(",")
res1: Array[String] = Array("", "a", "", "b")

@ ",a,,b,,".split(",", -1)  // limit=-1
res2: Array[String] = Array("", "a", "", "b", "", "")

Il semble bien connu que le comportement de Java est assez déroutant mais:

Le comportement ci-dessus peut être observé d'au moins Java 5 à Java 8.

Il y a eu une tentative de modification du comportement pour renvoyer un tableau vide lors du fractionnement d'une chaîne vide dans JDK-6559590 . Cependant, il a été rapidement rétabli dans JDK-8028321 lorsqu'il provoque une régression à divers endroits. Le changement ne fait jamais partie de la version initiale de Java 8.

Remarque: La méthode split n'était pas en Java depuis le début (ce n'est pas en 1.0.2 ) mais est en fait là depuis au moins 1.4 (par exemple voir JSR51 vers 2002). J'enquête toujours ...

Ce qui n'est pas clair, c'est pourquoi Java a choisi cela en premier lieu (je soupçonne que c'était à l'origine un oubli / bogue dans un "cas de pointe"), mais maintenant irrévocablement intégré dans le langage et il le reste .

Andy Hayden
la source
Je ne suis pas sûr que cela réponde à la question - bien que cela puisse être vrai pour l'exemple donné ici, cela n'aide pas avec le cas de la chaîne vide - "".split(",")renvoie toujours un tableau d'élément unique comme [""].
DaveyDaveDave
@DaveyDaveDave c'est le comportement attendu de toutes les autres langues. Le ",,,," est le comportement bizarre / différent de Scala, et disparate du cas "".
Andy Hayden le
0

Les chaînes vides n'ont pas de statut spécial lors de la division d'une chaîne. Vous pouvez utiliser:

Some(str)
  .filter(_ != "")
  .map(_.split(","))
  .getOrElse(Array())
Hanan Oanunu
la source