Pourquoi les chaînes vides sont-elles renvoyées dans les résultats de split ()?

120

Quel est le point de '/segment/segment/'.split('/')revenir ['', 'segment', 'segment', '']?

Remarquez les éléments vides. Si vous divisez sur un délimiteur qui se trouve à la position un et à la toute fin d'une chaîne, quelle valeur supplémentaire cela vous donne-t-il pour avoir la chaîne vide renvoyée de chaque extrémité?

orokusaki
la source
1
J'ai la même question et je l'ai recherchée pendant longtemps. Maintenant, je comprends que les résultats vides sont vraiment importants. Merci pour votre question.
emeraldhieu
2
Une solution consiste à utiliser strip()pour '/segment/segment/'.strip('/').split('/')
supprimer

Réponses:

178

str.splitcompléments str.join, donc

"/".join(['', 'segment', 'segment', ''])

vous récupère la chaîne d'origine.

Si les chaînes vides n'étaient pas là, la première et la dernière '/'seraient manquantes après lejoin()

John La Rooy
la source
11
Simple, mais répond pleinement à la question.
orokusaki
J'ai été choqué de découvrir que les citations bouclées sont en fait valides en Python ... mais, mais ... comment? Les documents ne semblent pas le mentionner.
Tim Pietzcker
@Tim, je n'ai aucune idée de la façon dont ces citations sont arrivées là-dedans: /
John La Rooy
7
Alors, vous n'utilisez pas Microsoft Word comme IDE Python? :)
Tim Pietzcker
1
@ aaa90210 qui a dit que les réponses simples n'étaient pas les meilleures? C'était un commentaire (d'abord, il y a 5 ans) sur la façon dont la réponse était simple, mais répondait pleinement à la question. Utiliser "mais" dans une phrase n'implique rien de mauvais. Une réponse non simple aurait pu être une réponse plus complète (par exemple, y compris des décisions pertinentes ou un PEP lié à la fonctionnalité indiquée).
orokusaki
88

Plus généralement, pour supprimer les chaînes vides renvoyées dans les split()résultats, vous souhaiterez peut-être examiner la filterfonction.

Exemple:

filter(None, '/segment/segment/'.split('/'))

Retour

['segment', 'segment']
Franck Dernoncourt
la source
3
Merci pour cela, je ne sais pas pourquoi cette réponse est si loin, tout le reste est rudimentaire.
Wedge
6
Si vous souhaitez collecter le résultat dans une liste au lieu d'obtenir un objet de filtre en sortie, placez toute la structure de filtre list(...).
Tim Visée
29

Il y a deux points principaux à considérer ici:

  • Il est raisonnable de s'attendre à ce que le résultat de '/segment/segment/'.split('/')soit égal à ['segment', 'segment'], mais cela perd alors des informations. Si cela split()fonctionnait comme vous le souhaitiez, si je vous dis cela a.split('/') == ['segment', 'segment'], vous ne pouvez pas me dire ce que ac'était.
  • Quel devrait être le résultat 'a//b'.split()? ['a', 'b']?, ou ['a', '', 'b']? Par exemple, faut-il split()fusionner les délimiteurs adjacents? Si tel est le cas, il sera très difficile d'analyser les données délimitées par un caractère, et certains champs peuvent être vides. Je suis assez sûr qu'il ya beaucoup de gens qui font veulent les valeurs vides dans le résultat pour le cas ci - dessus!

Au final, cela se résume à deux choses:

Cohérence: si j'ai des ndélimiteurs, dans a, je récupère les n+1valeurs après le split().

Il devrait être possible de faire des choses complexes, et faciles de faire des choses simples: si vous voulez ignorer les chaînes vides à la suite de split(), vous pouvez toujours faire:

def mysplit(s, delim=None):
    return [x for x in s.split(delim) if x]

mais si on ne veut pas ignorer les valeurs vides, on devrait pouvoir le faire.

Le langage doit choisir une définition de: split()il y a trop de cas d'utilisation différents pour satisfaire les exigences de chacun par défaut. Je pense que le choix de Python est bon, et le plus logique. (En passant, l'une des raisons pour lesquelles je n'aime pas C strtok()est parce qu'il fusionne les délimiteurs adjacents, ce qui rend extrêmement difficile de faire une analyse / tokenisation sérieuse avec lui.)

Il y a une exception: a.split()sans argument, serre les espaces blancs consécutifs, mais on peut affirmer que c'est la bonne chose à faire dans ce cas. Si vous ne voulez pas le comportement, vous pouvez toujours le faire a.split(' ').

Alok Singhal
la source
Pour ceux qui se demandent s'il est plus rapide de détruire les espaces dupliqués, puis de les fractionner, ou de les fractionner et de ne prendre que des chaînes non vides, voici ce que j'obtiens: python3 -m timeit "import re ; re.sub(' +', ' foo bar baz ', '').split(' ')"-> 875 nsec par boucle; python3 -m timeit "[token for token in ' foo bar baz '.split(' ') if token]"-> 616 nsec par boucle
s3cur3
8

Avoir x.split(y)toujours renvoyer une liste d' 1 + x.count(y)éléments est une régularité précieuse - comme @ gnibbler l'a déjà souligné, il fait splitet joinexact inverses les uns des autres (comme ils devraient évidemment l'être), il cartographie aussi précisément la sémantique de toutes sortes d'enregistrements joints par des délimiteurs ( comme csvles lignes de fichiers [[net de problèmes de citations]], les lignes d' /etc/groupUnix, etc.), il permet (comme la réponse de @ Roman l'a mentionné) de vérifier facilement (par exemple) les chemins absolus ou relatifs (dans les chemins de fichiers et les URL), et ainsi de suite.

Une autre façon de voir les choses est que vous ne devriez pas jeter des informations par la fenêtre sans aucun gain. Que gagnerait-on à faire l' x.split(y)équivalent x.strip(y).split(y)? Rien, bien sûr - il est facile d'utiliser la deuxième forme quand c'est ce que vous voulez dire, mais si la première forme a été arbitrairement comme signifiant le second, vous auriez beaucoup de travail à faire lorsque vous ne voulez que le premier ( ce qui est loin d'être rare, comme le souligne le paragraphe précédent).

Mais vraiment, penser en termes de régularité mathématique est le moyen le plus simple et le plus général de vous apprendre à concevoir des API praticables. Pour prendre un exemple différent, il est très important que, pour toute valeur xet y x == x[:y] + x[y:]- qui indique immédiatement pourquoi un extrême d'un découpage doit être exclu. Plus l'assertion invariante que vous pouvez formuler est simple, plus il est probable que la sémantique résultante soit ce dont vous avez besoin dans les utilisations réelles - une partie du fait mystique que les mathématiques sont très utiles pour traiter l'univers.

Essayez la formulation de l'invariant pour un splitdialecte dans lequel d' attaque et de fuite sont délimiteurs-cas spéciaux ... contre-exemple: les méthodes de chaînes telles que isspacene sont pas au maximum simple - x.isspace()est équivalent à x and all(c in string.whitespace for c in x)- ce leader stupide x andest la raison pour laquelle vous vous trouvez ainsi souvent vous codage not x or x.isspace(), pour revenir à la simplicité qui aurait dû être conçue dans les is...méthodes de chaîne (où une chaîne vide "est" tout ce que vous voulez - contrairement au sens du cheval de l'homme dans la rue, peut-être [[ensembles vides, comme zéro & c, ont toujours dérouté la plupart des gens ;-)]], mais se conformant pleinement au bon sens mathématique évident et raffiné ! -).

Alex Martelli
la source
5

Je ne sais pas quel type de réponse vous recherchez? Vous obtenez trois correspondances parce que vous avez trois délimiteurs. Si vous ne voulez pas celui-là vide, utilisez simplement:

'/segment/segment/'.strip('/').split('/')
Jamieb
la source
4
-1 parce que vous obtenez quatre correspondances et non trois, et cela ne répond pas vraiment à la question.
Roman
1
+1 pour contrer le négatif. Il n'a pas dit que vous obtiendriez trois résultats. Il a dit «trois matchs» pour «trois délimiteurs», ce qui me semble logique. Cependant, vous n'obtenez «quatre matchs» de rien. Vous obtenez cependant "quatre éléments" renvoyés dans votre résultat. En outre, il ne répond pas directement au «pourquoi», mais il fournit un moyen simple d'obtenir ce qu'il veut vraiment ... ce qui, je pense, ne mérite pas un vote défavorable. Si vous allez piquer quelqu'un (avec un vote négatif, rien de moins), soyez plus prudent! À votre santé! 8 ^)
kodybrown
@wasatchwizard Merci pour la clarification. J'apprécie la correction et la recommandation. Malheureusement, mon vote est maintenant verrouillé et ne peut pas être modifié.
Roman
J'adore votre solution - déshabiller puis diviser pour supprimer le résultat vide
Nam G VU
5

Eh bien, cela vous permet de savoir qu'il y avait un délimiteur là-bas. Ainsi, voir 4 résultats vous permet de savoir que vous avez 3 délimiteurs. Cela vous donne le pouvoir de faire ce que vous voulez avec ces informations, plutôt que de laisser Python supprimer les éléments vides, puis de vous faire vérifier manuellement les délimiteurs de début ou de fin si vous avez besoin de le savoir.

Exemple simple: Supposons que vous souhaitiez vérifier les noms de fichiers absolus et relatifs. De cette façon, vous pouvez tout faire avec le fractionnement, sans avoir à vérifier également quel est le premier caractère de votre nom de fichier.

romain
la source
1

Considérez cet exemple minimal:

>>> '/'.split('/')
['', '']

splitdoit vous donner ce qui est avant et après le délimiteur '/', mais il n'y a pas d'autres caractères. Il doit donc vous donner la chaîne vide, qui précède et suit techniquement le '/', car '' + '/' + '' == '/'.

Timgeb
la source