web-dev-qa-db-fra.com

Lors du fractionnement 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 chaîne. J'ai trouvé que ''.split() renvoie une liste vide, [], Tandis que ''.split('\n') renvoie ['']. Y a-t-il une raison spécifique pour une telle différence?

Et existe-t-il un moyen plus pratique de compter les lignes d'une chaîne?

131
godice

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

La méthode str.split () a deux algorithmes. Si aucun argument n'est fourni, il se scinde en plusieurs fois des espaces blancs. Cependant, si un argument est donné, il est traité comme un simple délimiteur sans exécution répétée.

Dans le cas de la division d'une chaîne vide, le premier mode (pas d'argument) renverra une liste vide car l'espace est mangé et il n'y a pas de valeurs à mettre dans la liste des résultats.

En revanche, le deuxième mode (avec un argument tel que \n) Produira le premier champ vide. Considérez si vous aviez écrit '\n'.split('\n'), vous auriez deux champs (un fractionné, 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 second 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ésultats est supérieur de un au nombre de délimiteurs. Pensez à couper une corde. Si vous ne faites pas de coupes, vous avez une pièce. Faire une coupe, donne deux pièces. Faire deux coupes, donne trois pièces. Et c'est donc avec la méthode str.split (délimiteur) de Python:

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

Question: Et existe-t-il un moyen plus pratique de compter les lignes d'une chaîne?

Oui, il y a deux façons simples. On utilise str.count () et l'autre utilise str.splitlines () . Les deux manières donneront la même réponse à moins que la dernière ligne ne manque de \n. Si la nouvelle ligne manquante est manquante, l'approche str.splitlines donnera la réponse exacte. Une technique plus rapide et précise utilise également la méthode count 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 existe-t-il deux algorithmes très différents, combinés dans une même fonction?

La signature de str.split a environ 20 ans et un certain nombre d'API de cette époque sont strictement pragmatiques. Bien que n'étant pas parfait, la signature de la méthode n'est pas "terrible" non plus. Dans la plupart des cas, 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 casser ces chaînes dans des 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 utilisateurs ont tendance à interpréter correctement les instructions en tant que "fractionne une ligne en champs".

L'outil texte en colonnes de Microsoft Excel a fait un choix similaire de l'API et incorpore les deux algorithmes de fractionnement dans le même outil. Les gens semblent modéliser mentalement la division de champ en tant que concept unique, même si plusieurs algorithmes sont impliqués.

214

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

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

Si sep n'est pas spécifié ou est défini sur None, un algorithme de scission différent est appliqué: les suites d'espaces consécutives sont considérées comme un séparateur unique et le résultat ne contient aucune chaîne vide au début ou à la fin si la chaîne est précédée ou terminée. 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 que ce soit plus clair, la fonction split() implémente deux algorithmes de fractionnement différents et utilise la présence d'un argument pour décider lequel exécuter. C'est peut-être parce que cela permet d'optimiser celui qui ne contient pas plus d'arguments que celui qui contient des arguments; Je ne sais pas.

28
unwind

.split() sans paramètres essaie d'être intelligent. Il se scinde en espaces, onglets, espaces, sauts de ligne, etc., et ignore également toutes les chaînes vides.

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

Essentiellement, .split() sans paramètres est utilisé pour extraire les mots d'une chaîne, contrairement à .split() avec des paramètres qui prennent une chaîne et la divisent.

C'est la raison de la différence.

Et oui, compter les lignes en scindant 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.

3
Lennart Regebro

Utilisez count():

s = "Line 1\nLine2\nLine3"
n_lines = s.count('\n') + 1
2
Gareth Webber
>>> 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 de \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.

1
Bakuriu

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 count intégré est plus approprié, en fait

0
Jakub M.