Lors du fractionnement d'une chaîne vide en Python, pourquoi split () renvoie-t-il une liste vide alors que split ('\ n') renvoie ['']?

156

J'utilise split('\n')pour obtenir des lignes dans une chaîne, et j'ai trouvé que ''.split()renvoie une liste vide [], tandis que ''.split('\n')renvoie ['']. Y a-t-il une raison spécifique à une telle différence?

Et y a-t-il un moyen plus pratique de compter les lignes dans une chaîne?

godice
la source

Réponses:

248

Question: J'utilise split ('\ n') pour obtenir des lignes dans une chaîne, et j'ai trouvé que '' .split () renvoie une liste vide [], tandis que '' .split ('\ n') renvoie [''] .

La méthode str.split () a deux algorithmes. Si aucun argument n'est donné, il se divise sur des exécutions répétées d'espaces. Cependant, si un argument est donné, il est traité comme un délimiteur unique sans exécution répétée.

Dans le cas du fractionnement d'une chaîne vide, le premier mode (sans argument) renverra une liste vide car l'espace blanc est mangé et il n'y a aucune valeur à mettre dans la liste de résultats.

En revanche, le second mode (avec un argument tel que \n) produira le premier champ vide. Considérez que si vous aviez écrit '\n'.split('\n'), vous obtiendriez deux champs (une division, vous donne deux moitiés).

Question: Y a-t-il une raison spécifique à une telle différence?

Ce premier mode est utile lorsque les données sont alignées dans des colonnes avec des quantités variables d'espaces. Par exemple:

>>> data = '''\
Shasta      California     14,200
McKinley    Alaska         20,300
Fuji        Japan          12,400
'''
>>> for line in data.splitlines():
        print line.split()

['Shasta', 'California', '14,200']
['McKinley', 'Alaska', '20,300']
['Fuji', 'Japan', '12,400']

Le deuxième mode est utile pour les données délimitées telles que CSV où des virgules répétées indiquent des champs vides. Par exemple:

>>> data = '''\
Guido,BDFL,,Amsterdam
Barry,FLUFL,,USA
Tim,,,USA
'''
>>> for line in data.splitlines():
        print line.split(',')

['Guido', 'BDFL', '', 'Amsterdam']
['Barry', 'FLUFL', '', 'USA']
['Tim', '', '', 'USA']

Notez que le nombre de champs de résultat est supérieur de un au nombre de délimiteurs. Pensez à couper une corde. Si vous ne faites aucune coupure, vous n'en avez qu'une seule pièce. Faire une coupe, donne deux morceaux. Faire deux coupes, donne trois morceaux. Et il en est de même avec la méthode str.split (délimiteur) de Python :

>>> ''.split(',')       # No cuts
['']
>>> ','.split(',')      # One cut
['', '']
>>> ',,'.split(',')     # Two cuts
['', '', '']

Question: Et y a-t-il un moyen plus pratique de compter les lignes dans une chaîne?

Oui, il existe plusieurs moyens simples. L'un utilise str.count () et l'autre utilise str.splitlines () . Les deux méthodes donneront la même réponse à moins que la dernière ligne ne manque le \n. Si la dernière nouvelle ligne est manquante, l' approche str.splitlines donnera la réponse exacte. Une technique plus rapide qui est également précise utilise la méthode de comptage, mais la corrige ensuite pour la nouvelle ligne finale:

>>> data = '''\
Line 1
Line 2
Line 3
Line 4'''

>>> data.count('\n')                               # Inaccurate
3
>>> len(data.splitlines())                         # Accurate, but slow
4
>>> data.count('\n') + (not data.endswith('\n'))   # Accurate and fast
4    

Question de @Kaz: Pourquoi diable y a-t-il deux algorithmes très différents en une seule fonction?

La signature de str.split a environ 20 ans, et un certain nombre d'API de cette époque sont strictement pragmatiques. Bien qu'elle ne soit pas parfaite, la signature de la méthode n'est pas non plus "terrible". Pour la plupart, les choix de conception d'API de Guido ont résisté à l'épreuve du temps.

L'API actuelle n'est pas sans avantages. Considérez des chaînes telles que:

ps_aux_header  = "USER               PID  %CPU %MEM      VSZ"
patient_header = "name,age,height,weight"

Lorsqu'on leur demande de diviser ces chaînes en champs, les gens ont tendance à décrire les deux en utilisant le même mot anglais, «split». Lorsqu'on leur demande de lire du code tel que fields = line.split() ou fields = line.split(','), les gens ont tendance à interpréter correctement les déclarations comme "divise une ligne en champs".

L' outil de texte en colonnes de Microsoft Excel a fait un choix d'API similaire et intègre les deux algorithmes de fractionnement dans le même outil. Les gens semblent modéliser mentalement le fractionnement de champ comme un concept unique, même si plus d'un algorithme est impliqué.

Raymond Hettinger
la source
28

Cela semble être simplement la façon dont cela est censé fonctionner, selon la documentation :

Fractionner une chaîne vide avec un séparateur spécifié renvoie [''].

Si sep n'est pas spécifié ou vaut None, un algorithme de fractionnement différent est appliqué: les exécutions d'espaces blancs consécutifs sont considérées comme un seul séparateur, et le résultat ne contiendra aucune chaîne vide au début ou à la fin si la chaîne comporte des espaces blancs au début ou à la fin. Par conséquent, le fractionnement d'une chaîne vide ou d'une chaîne composée uniquement d'espaces avec un séparateur Aucun renvoie [].

Ainsi, pour clarifier les choses, la split()fonction implémente deux algorithmes de fractionnement différents et utilise la présence d'un argument pour décider lequel exécuter. Cela peut être dû au fait qu'il permet d'optimiser celui pour aucun argument de plus que celui avec des arguments; Je ne sais pas.

se détendre
la source
4

.split()sans paramètres essaie d'être intelligent. Il se divise sur tous les espaces, tabulations, espaces, sauts de ligne, etc., et il ignore également toutes les chaînes vides à la suite de cela.

>>> "  fii    fbar \n bopp ".split()
['fii', 'fbar', 'bopp']

Essentiellement, .split()sans paramètres sont utilisés pour extraire des mots d'une chaîne, par opposition à .split()des paramètres qui ne prennent qu'une chaîne et la divisent.

C'est la raison de la différence.

Et oui, compter les lignes par division n'est pas un moyen efficace. Comptez le nombre de sauts de ligne et ajoutez-en un si la chaîne ne se termine pas par un saut de ligne.

Lennart Regebro
la source
2

Utilisez count():

s = "Line 1\nLine2\nLine3"
n_lines = s.count('\n') + 1
Gareth Webber
la source
4
Le + 1 ne doit être fait que si le texte ne se termine pas par '\ n'.
Lennart Regebro
8
Eh bien, si elle se termine par "\ n", la dernière ligne est une ligne vide. Bien qu'inutile, cela compte toujours comme ligne, non?
Jakub M.
2
non. quand j'écris 3 lignes de texte dans un fichier et que je termine chacune d'elles par un saut de ligne, alors je dirais que le fichier contient 3 lignes. sous unix, il est préférable que le fichier texte se termine toujours par un saut de ligne. sinon, cat filevotre ligne de commande brouille et subversion se plaint. vi en ajoute toujours un.
user829755
2
>>> print str.split.__doc__
S.split([sep [,maxsplit]]) -> list of strings

Return a list of the words in the string S, using sep as the
delimiter string.  If maxsplit is given, at most maxsplit
splits are done. If sep is not specified or is None, any
whitespace string is a separator and empty strings are removed
from the result.

Notez la dernière phrase.

Pour compter les lignes, vous pouvez simplement compter combien \nil y en a:

line_count = some_string.count('\n') + some_string[-1] != '\n'

La dernière partie prend en compte la dernière ligne qui ne se termine pas par \n, même si cela signifie que Hello, World!et Hello, World!\nont le même nombre de lignes (ce qui pour moi est raisonnable), sinon vous pouvez simplement ajouter 1au nombre de \n.

Bakuriu
la source
0

Pour compter les lignes, vous pouvez compter le nombre de sauts de ligne:

n_lines = sum(1 for s in the_string if s == "\n") + 1 # add 1 for last line

Modifier :

L'autre réponse avec intégré countest plus appropriée, en fait

Jakub M.
la source
3
En plus de simplement utiliser count, intles bools peuvent être ajoutés (en fait, ils sont sous-classes ), de sorte que genexp peut être écrit sous la forme sum(s == "\n" for s in the_string).
lvc
En ce moment, vous ne comptez que les lignes vides?
Thijs van Dien
Oui, je ne supprime aucune ligne vide
Jakub M.