Fractionner par virgule et supprimer les espaces en Python

346

J'ai du code python qui se divise en virgule, mais ne supprime pas l'espace blanc:

>>> string = "blah, lots  ,  of ,  spaces, here "
>>> mylist = string.split(',')
>>> print mylist
['blah', ' lots  ', '  of ', '  spaces', ' here ']

Je préfère me retrouver avec des espaces supprimés comme ceci:

['blah', 'lots', 'of', 'spaces', 'here']

Je suis conscient que je pourrais parcourir la liste et supprimer () chaque élément mais, comme il s'agit de Python, je suppose qu'il existe une manière plus rapide, plus facile et plus élégante de le faire.

Mr_Chimp
la source

Réponses:

594

Utilisez la compréhension de liste - plus simple et tout aussi facile à lire qu'une forboucle.

my_string = "blah, lots  ,  of ,  spaces, here "
result = [x.strip() for x in my_string.split(',')]
# result is ["blah", "lots", "of", "spaces", "here"]

Voir: Documents Python sur la compréhension de liste
Une bonne explication de 2 secondes sur la compréhension de liste.

Sean Vieira
la source
1
Super bon! J'ai ajouté un élément comme suit pour se débarrasser des entrées de la liste vide. > text = [x.strip () pour x dans text.split ('.') si x! = '']
RandallShanePhD
@Sean: le code python invalide / incomplet était-il votre "intention originale de la publication"? Selon les branleurs de la revue, il s'agissait de: stackoverflow.com/review/suggested-edits/21504253 . Pouvez-vous s'il vous plaît leur dire le contraire en effectuant la correction s'ils se trompent (encore)?
Forage
L'original a été copié-collé à partir d'un REPL (si je me souviens bien) et le but était de comprendre le concept sous-jacent (utiliser la compréhension de liste pour effectuer une opération) - mais vous avez raison, il est plus logique si vous voyez cette compréhension de liste produit une nouvelle liste.
Sean Vieira
24

Fractionnez en utilisant une expression régulière. Remarque J'ai rendu le cas plus général avec des espaces de début. La compréhension de la liste consiste à supprimer les chaînes nulles à l'avant et à l'arrière.

>>> import re
>>> string = "  blah, lots  ,  of ,  spaces, here "
>>> pattern = re.compile("^\s+|\s*,\s*|\s+$")
>>> print([x for x in pattern.split(string) if x])
['blah', 'lots', 'of', 'spaces', 'here']

Cela fonctionne même si ^\s+ne correspond pas:

>>> string = "foo,   bar  "
>>> print([x for x in pattern.split(string) if x])
['foo', 'bar']
>>>

Voici pourquoi vous avez besoin de ^ \ s +:

>>> pattern = re.compile("\s*,\s*|\s+$")
>>> print([x for x in pattern.split(string) if x])
['  blah', 'lots', 'of', 'spaces', 'here']

Voir les principaux espaces de bla?

Clarification: ci-dessus utilise l'interpréteur Python 3, mais les résultats sont les mêmes dans Python 2.

tbc0
la source
8
Je pense que [x.strip() for x in my_string.split(',')]c'est plus pythonique pour la question posée. Il y a peut-être des cas où ma solution est nécessaire. Je mettrai à jour ce contenu si j'en rencontre un.
tbc0
Pourquoi est-il ^\s+nécessaire? J'ai testé votre code sans lui et cela ne fonctionne pas, mais je ne sais pas pourquoi.
laike9m
Si j'utilise re.compile("^\s*,\s*$"), le résultat est [' blah, lots , of , spaces, here '].
laike9m
@ laike9m, j'ai mis à jour ma réponse pour vous montrer la différence. ^\s+fait du. Comme vous pouvez le constater par vous-même, ^\s*,\s*$ne renvoie pas non plus les résultats souhaités. Donc, si vous voulez diviser avec une expression rationnelle, utilisez ^\s+|\s*,\s*|\s+$.
tbc0
La première correspondance est vide si le motif de tête (^ \ s +) ne correspond pas, vous obtenez donc quelque chose comme ['', 'foo', 'bar'] pour la chaîne "foo, bar".
Steeve McCauley
21

Je suis venu ajouter:

map(str.strip, string.split(','))

mais vu qu'il avait déjà été mentionné par Jason Orendorff dans un commentaire .

En lisant le commentaire de Glenn Maynard dans la même réponse suggérant des compréhensions de liste sur la carte, j'ai commencé à me demander pourquoi. Je supposais qu'il voulait dire pour des raisons de performance, mais bien sûr il aurait pu vouloir dire pour des raisons stylistiques, ou autre chose (Glenn?).

Un test rapide (éventuellement imparfait?) Sur ma box appliquant les trois méthodes en boucle a donc révélé:

[word.strip() for word in string.split(',')]
$ time ./list_comprehension.py 
real    0m22.876s

map(lambda s: s.strip(), string.split(','))
$ time ./map_with_lambda.py 
real    0m25.736s

map(str.strip, string.split(','))
$ time ./map_with_str.strip.py 
real    0m19.428s

fabrication map(str.strip, string.split(',')) le gagnant, même s'il semble qu'ils sont tous dans le même stade.

Certes, la carte (avec ou sans lambda) ne doit pas nécessairement être exclue pour des raisons de performances, et pour moi, c'est au moins aussi clair qu'une compréhension de liste.

Éditer:

Python 2.6.5 sur Ubuntu 10.04

Sean
la source
15

Supprimez simplement l'espace blanc de la chaîne avant de le diviser.

mylist = my_string.replace(' ','').split(',')
user489041
la source
10
Type de problème si les éléments séparés par des virgules contiennent des espaces intégrés, par exemple "you just, broke this".
Robert Rossney
1
Geeze, un -1 pour cela. Vous êtes durs. Cela a résolu son problème, à condition que ses données d'échantillonnage ne soient que des mots simples et qu'il n'y avait aucune spécification que les données seraient des phrases. Mais w / e, je suppose que c'est comme ça que vous roulez ici.
user489041
Merci quand même, utilisateur. Pour être honnête, j'ai demandé spécifiquement split, puis strip () et strip supprime les espaces de début et de fin et ne touche rien entre les deux. Un léger changement et votre réponse fonctionnerait parfaitement, cependant: mylist = mystring.strip (). Split (',') même si je ne sais pas si cela est particulièrement efficace.
Mr_Chimp
12

Je sais que cela a déjà été répondu, mais si vous arrêtez de faire beaucoup de choses, les expressions régulières peuvent être une meilleure façon de procéder:

>>> import re
>>> re.sub(r'\s', '', string).split(',')
['blah', 'lots', 'of', 'spaces', 'here']

Le \scorrespond à n'importe quel caractère d'espacement, et nous le remplaçons simplement par une chaîne vide ''. Vous pouvez trouver plus d'informations ici: http://docs.python.org/library/re.html#re.sub

Brad Montgomery
la source
3
Votre exemple ne fonctionnerait pas sur les chaînes contenant des espaces. "pour, par exemple ceci, un" deviendrait "pour", "par exemple", "un". Cela ne veut pas dire que c'est une mauvaise solution (cela fonctionne parfaitement sur mon exemple), cela dépend simplement de la tâche à accomplir!
Mr_Chimp
Oui, c'est très correct! Vous pouvez probablement ajuster l'expression rationnelle pour qu'il puisse gérer les chaînes avec des espaces, mais si la compréhension de la liste fonctionne, je dirais que vous y tenez;)
Brad Montgomery
2
import re
result=[x for x in re.split(',| ',your_string) if x!='']

cela fonctionne bien pour moi.

Zieng
la source
2

re (comme dans les expressions régulières) permet de diviser plusieurs caractères à la fois:

$ string = "blah, lots  ,  of ,  spaces, here "
$ re.split(', ',string)
['blah', 'lots  ', ' of ', ' spaces', 'here ']

Cela ne fonctionne pas bien pour votre exemple de chaîne, mais fonctionne bien pour une liste séparée par des virgules. Pour votre exemple de chaîne, vous pouvez combiner la puissance re.split pour diviser les motifs regex pour obtenir un effet "split-on-this-or-that".

$ re.split('[, ]',string)
['blah',
 '',
 'lots',
 '',
 '',
 '',
 '',
 'of',
 '',
 '',
 '',
 'spaces',
 '',
 'here',
 '']

Malheureusement, c'est moche, mais filterça fera l'affaire:

$ filter(None, re.split('[, ]',string))
['blah', 'lots', 'of', 'spaces', 'here']

Voila!

Dannid
la source
2
Pourquoi pas juste re.split(' *, *', string)?
Paul Tomblin
4
@PaulTomblin bonne idée. On peut aussi avoir fait ça: re.split('[, ]*',string)pour le même effet.
Dannid
Dannid J'ai réalisé après avoir écrit qu'il ne supprime pas les espaces au début et à la fin comme le fait la réponse de @ tbc0.
Paul Tomblin
@PaulTomblinheh, et ma réfutation [, ]*laisse une chaîne vide à la fin de la liste. Je pense que le filtre est toujours une bonne chose à jeter là-dedans, ou s'en tenir à la compréhension de la liste comme le fait la première réponse.
Dannid
1

map(lambda s: s.strip(), mylist)serait un peu mieux qu'une boucle explicite. Ou pour le tout à la fois:map(lambda s:s.strip(), string.split(','))

user470379
la source
10
Astuce: chaque fois que vous vous retrouvez à utiliser map, en particulier si vous l'utilisez lambda, vérifiez à nouveau si vous devez utiliser une liste de compréhension.
Glenn Maynard,
11
Vous pouvez éviter le lambda avec map(str.strip, s.split(',')).
Jason Orendorff
1
s = 'bla, buu, jii'

sp = []
sp = s.split(',')
for st in sp:
    print st
Parikshit Pandya
la source
1
import re
mylist = [x for x in re.compile('\s*[,|\s+]\s*').split(string)]

Simplement, une virgule ou au moins un espace blanc avec / sans espaces blancs précédents / suivants.

S'il vous plaît essayez!

GyuHyeon Choi
la source
0

map(lambda s: s.strip(), mylist)serait un peu mieux qu'une boucle explicite.
Ou pour le tout à la fois:

map(lambda s:s.strip(), string.split(','))

C'est essentiellement tout ce dont vous avez besoin.

DJbigpenis
la source