Podziel przez przecinek i usuń białe znaki w Pythonie


346

Mam kod Pythona, który dzieli się przecinkiem, ale nie usuwa białych znaków:

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

Wolę raczej usunąć białe znaki w następujący sposób:

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

Wiem, że mogłem przeglądać listę i strip () każdego elementu, ale ponieważ jest to Python, domyślam się, że istnieje szybszy, łatwiejszy i bardziej elegancki sposób na zrobienie tego.

Odpowiedzi:


594

Używaj rozumienia list - prostsze i tak samo łatwe do odczytania jak forpętla.

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

Zobacz: Dokumenty Pythona dotyczące rozumienia listy
Dobre 2-sekundowe objaśnienie rozumienia listy.


1
Super dobrze! Dodałem jeden element w następujący sposób, aby pozbyć się pustych pozycji na liście. > text = [x.strip () dla x w text.split ('.') if x! = '']
RandallShanePhD

@Sean: czy niepoprawny / niekompletny kod Pythona był twoją „pierwotną intencją posta”? Według opinii wankers było to: stackoverflow.com/review/suggested-edits/21504253 . Czy możesz im powiedzieć inaczej, dokonując korekty, jeśli się mylą (ponownie)?
Pasza

Oryginał został skopiowany z REPL (o ile dobrze pamiętam), a celem było zrozumienie podstawowej koncepcji (użycie operacji interpretacji listy do wykonania operacji) - ale masz rację, bardziej sensowne jest zobaczenie tej listy tworzy nową listę.
Sean Vieira

24

Podziel za pomocą wyrażenia regularnego. Uwaga: Sprawę uczyniłem bardziej ogólną z wiodącymi spacjami. Zrozumienie listy polega na usunięciu łańcuchów zerowych z przodu iz tyłu.

>>> 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']

Działa to, nawet jeśli ^\s+nie pasuje:

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

Oto dlaczego potrzebujesz ^ \ s +:

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

Widzisz wiodące miejsca w bla?

Wyjaśnienie: powyżej używa interpretera języka Python 3, ale wyniki są takie same w języku Python 2.


8
Uważam, że [x.strip() for x in my_string.split(',')]pytanie jest bardziej pytoniczne. Może są przypadki, w których moje rozwiązanie jest konieczne. Zaktualizuję tę zawartość, jeśli napotkam jedną.
tbc0

Dlaczego jest to ^\s+konieczne? Przetestowałem twój kod bez niego i nie działa, ale nie wiem dlaczego.
laike9m

Jeśli użyję re.compile("^\s*,\s*$"), wynik będzie [' blah, lots , of , spaces, here '].
laike9m

@ laike9m, zaktualizowałem swoją odpowiedź, aby pokazać różnicę. ^\s+robi. Jak widać, ^\s*,\s*$również nie zwraca pożądanych rezultatów. Więc jeśli chcesz podzielić za pomocą wyrażenia regularnego, użyj ^\s+|\s*,\s*|\s+$.
tbc0

Pierwsze dopasowanie jest puste, jeśli wiodący wzorzec (^ \ s +) nie pasuje, więc otrzymujesz coś w rodzaju [”,„ foo ”,„ bar ”] dla ciągu„ foo, bar ”.
Steeve McCauley,

21

Przyszedłem dodać:

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

ale zobaczył, że wspomniał o tym Jason Orendorff w komentarzu .

Czytając komentarz Glenna Maynarda w tej samej odpowiedzi, sugerujący objaśnienia listy na mapie, zacząłem się zastanawiać, dlaczego. Zakładałem, że miał na myśli ze względu na wydajność, ale oczywiście mógł mieć na myśli ze względów stylistycznych lub czegoś innego (Glenn?).

Tak więc szybki (prawdopodobnie wadliwy?) Test na moim urządzeniu przy użyciu trzech metod w pętli ujawnił:

[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

co czyni map(str.strip, string.split(','))zwycięzcę, choć wydaje się, że wszyscy są na tym samym boisku.

Z pewnością jednak mapa (z lub bez lambda) niekoniecznie musi być wykluczona ze względu na wydajność, a dla mnie jest co najmniej tak klarowna jak lista.

Edytować:

Python 2.6.5 na Ubuntu 10.04


15

Po prostu usuń białą spację z łańcucha przed podzieleniem.

mylist = my_string.replace(' ','').split(',')

10
Rodzaj problemu, jeśli elementy oddzielone przecinkami zawierają spacje, np "you just, broke this".
Robert Rossney,

1
Rany, -1 za to. Jesteście twardi. Rozwiązało to jego problem, pod warunkiem, że jego przykładowe dane były tylko pojedynczymi słowami i nie było specyfikacji, że dane będą frazami. Ale w / e, myślę, że tak właśnie się tu toczycie.
user489041,

Cóż, w każdym razie dzięki, użytkownik. Aby być uczciwym, szczególnie poprosiłem o podział, a następnie strip () i strip usuwa wiodące i końcowe białe spacje i nie dotyka niczego pomiędzy. Niewielka zmiana, a twoja odpowiedź działałaby idealnie: mylist = mystring.strip (). Split (','), chociaż nie wiem, czy jest to szczególnie skuteczne.
Mr_Chimp

12

Wiem, że już na nie odpowiedziano, ale jeśli często to robisz, wyrażenia regularne mogą być lepszym sposobem:

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

\sDopasowuje dowolny znak spacją, a my po prostu zastąpić go z pustym ciągiem ''. Więcej informacji można znaleźć tutaj: http://docs.python.org/library/re.html#re.sub


3
Twój przykład nie działa na ciągach zawierających spacje. „na przykład jeden” stałby się „za”, „egzaminem”, „jednym”. Nie mówię, że jest to ZŁE rozwiązanie (działa idealnie na moim przykładzie), to po prostu zależy od zadania, które masz pod ręką!
Mr_Chimp

Tak, to bardzo poprawne! Prawdopodobnie możesz dostosować wyrażenie regularne, aby mogło obsługiwać ciągi ze spacjami, ale jeśli zrozumienie listy działa, powiedziałbym, że się trzymaj;)
Brad Montgomery

2
import re
result=[x for x in re.split(',| ',your_string) if x!='']

to działa dobrze dla mnie.


2

re (jak w wyrażeniach regularnych) pozwala na dzielenie wielu znaków jednocześnie:

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

Nie działa to dobrze dla przykładowego ciągu, ale działa dobrze w przypadku listy oddzielonej przecinkami. W przykładowym ciągu możesz połączyć moc re.split w celu podzielenia wzorców wyrażeń regularnych, aby uzyskać efekt „podziel na to lub tamto”.

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

Niestety, to brzydkie, ale filterzałatwi sprawę:

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

Voila!


2
Dlaczego nie tylko re.split(' *, *', string)?
Paul Tomblin

4
@PaulTomblin dobry pomysł. Można to również zrobić: re.split('[, ]*',string)dla tego samego efektu.
Dannid,

Dannid zdałem sobie sprawę po napisaniu, że nie usuwa białych znaków na początku i na końcu, jak odpowiedź @ tbc0.
Paul Tomblin,

@PaulTomblinheh, a moja odmowa [, ]*pozostawia pusty ciąg na końcu listy. Myślę, że filtr jest nadal fajną rzeczą do wrzucenia lub trzymania się listy, tak jak robi to najlepsza odpowiedź.
Dannid,

1

map(lambda s: s.strip(), mylist)byłoby trochę lepsze niż jawne zapętlenie. Lub dla całej rzeczy naraz:map(lambda s:s.strip(), string.split(','))


10
Wskazówka: za każdym razem map, gdy korzystasz lambdaz niego , szczególnie jeśli korzystasz z niego, dwukrotnie sprawdź, czy powinieneś używać rozumienia listy.
Glenn Maynard

11
Możesz uniknąć lambda za pomocą map(str.strip, s.split(',')).
Jason Orendorff,


1
import re
mylist = [x for x in re.compile('\s*[,|\s+]\s*').split(string)]

Po prostu przecinek lub przynajmniej jedna biała spacja z / bez poprzedzających / następnych białych spacji.

Proszę spróbować!


0

map(lambda s: s.strip(), mylist)byłoby trochę lepsze niż jawne zapętlenie.
Lub dla całej rzeczy naraz:

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

To w zasadzie wszystko, czego potrzebujesz.

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.