179 votes

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

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

Existe-t-il un moyen plus pratique de compter les lignes d'une chaîne de caractères ?

2 votes

269voto

Raymond Hettinger Points 50330

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

Le site str.split() a deux algorithmes. Si aucun argument n'est donné, elle divise sur des passages répétés d'espaces blancs. En revanche, si un argument est donné, il est traité comme un seul délimiteur, sans répétition.

Dans le cas de la division d'une chaîne vide, le premier mode (sans argument) renverra une liste vide parce que les espaces blancs sont mangés et qu'il n'y a pas de valeurs à 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 obtiendrez deux champs (un divisé, ce qui vous donne deux moitiés).

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

Ce premier mode est utile lorsque les données sont alignées dans des colonnes comportant des quantités variables d'espaces blancs. 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ù les virgules répétées indiquent les 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']

Remarque : le nombre de champs de résultat est supérieur d'une unité au nombre de délimiteurs. Imaginez que vous coupez une corde. Si vous ne faites aucune coupe, vous avez un morceau. Si vous faites une coupe, vous obtenez deux morceaux. En faisant deux coupes, on obtient trois morceaux. Il en va de même avec la méthode Python str.split(delimiter) méthode :

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

Question : Existe-t-il un moyen plus pratique de compter les lignes d'une chaîne de caractères ?

Oui, il y a quelques moyens faciles. La première utilise str.count() et les autres utilisations str.splitlines() . Les deux méthodes donnent la même réponse, à moins que la dernière ligne ne soit manquante. \n . Si le dernier saut de ligne est manquant, le 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 le saut de ligne final :

>>> 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 deux algorithmes très différents sont-ils intégrés dans une seule fonction ?

La signature pour 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 "terrible" non plus. Pour l'essentiel, les choix de Guido en matière de conception d'API ont résisté à l'épreuve du temps.

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

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

Lorsqu'on leur demande de décomposer ces chaînes en champs, les gens ont tendance à les décrire en utilisant le même mot anglais, "split". Lorsqu'on leur demande de lire un 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".

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

31voto

unwind Points 181987

Il semble que ce soit simplement la façon dont c'est censé fonctionner, selon la documentation :

La division d'une chaîne de caractères vide avec un séparateur spécifié retourne [''] .

Si sep n'est pas spécifié ou est None, un algorithme de division différent est appliqué : les séries d'espaces blancs consécutifs sont considérées comme un séparateur unique, et le résultat ne contiendra aucune chaîne vide au début ou à la fin si la chaîne a des espaces blancs en tête ou en queue. Par conséquent, la division d'une chaîne vide ou d'une chaîne constituée uniquement d'espaces avec un séparateur None renvoie [].

Donc, pour être plus clair, le split() implémente deux algorithmes de fractionnement différents, et utilise la présence d'un argument pour décider lequel exécuter. Cela pourrait être dû au fait que cela permet d'optimiser davantage celui sans argument que celui avec argument ; je ne sais pas.

5voto

Lennart Regebro Points 52510

.split() sans paramètres essaie d'être intelligente. Il coupe les espaces blancs, les tabulations, les espaces, les retours à la ligne, etc., et il saute également toutes les chaînes vides.

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

Essentiellement, .split() sans paramètres sont utilisés pour extraire des mots d'une chaîne de caractères, par opposition à .split() avec des paramètres qui prend juste une chaîne de caractères et la divise.

C'est la raison de cette différence.

Et oui, compter les lignes en les divisant n'est pas une méthode 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.

2voto

Gareth Webber Points 1398

Utilisez count() :

s = "Line 1\nLine2\nLine3"
n_lines = s.count('\n') + 1

4 votes

Le + 1 ne doit être fait que si le texte ne se termine pas par ''. \n '.

8 votes

Eh bien, si ça se termine par " \n "alors la dernière ligne est une ligne vide. Bien qu'inutile, elle compte quand même comme une ligne, non ?

3 votes

non. lorsque j'écris 3 lignes de texte dans un fichier et que je termine chacune d'entre elles par un saut de ligne, je dirais que le fichier contient 3 lignes. sous unix, il est préférable qu'un fichier texte se termine toujours par un saut de ligne. dans le cas contraire, le fichier se termine par un saut de ligne. cat file déforme votre ligne de commande et subversion se plaint. vi ajoute toujours un.

2voto

Bakuriu Points 22607
>>> 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 \n sont là :

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!\n ont le même nombre de lignes (ce qui pour moi est raisonnable), sinon vous pouvez simplement ajouter 1 au compte de \n .

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