2017-02-19 25 views
14

mam listy ciągów znaków takich jak ta:Jak poprawnie podzielić tę listę ciągów?

['z+2-44', '4+55+z+88'] 

Jak mogę podzielić ten ciągi w wykazie taki sposób, że będzie to coś jak

[['z','+','2','-','44'],['4','+','55','+','z','+','88']] 

Próbowałem przy użyciu metody split już jednak to dzieli 44 na 4 i 4, i nie jestem pewien, co jeszcze spróbować.

+1

Podano, że specyfikacja jest niekompletna. A co z operatorami matematyki * i /? A co ze zmiennymi a, b i c? Czy pi jest stałą, zmienną lub p * i? Podane pytanie przyciągnie odpowiedzi, które mogą nie być przydatne we wszystkich przypadkach. –

+0

@martineau Uważam, że pytanie [this] (http://stackoverflow.com/questions/4736/learning-regular-expressions) nie jest właściwym duplikatem. – Kasramvd

+0

@Kasramvd: Byłbym zainteresowany usłyszeniem, dlaczego tak myślisz. – martineau

Odpowiedz

26

Można użyć wyrażenia regularnego:

import re 
lst = ['z+2-44', '4+55+z+88'] 
[re.findall('\w+|\W+', s) for s in lst] 
# [['z', '+', '2', '-', '44'], ['4', '+', '55', '+', 'z', '+', '88']] 

\w+|\W+ pasuje do wzorca, który składa się zarówno ze znaków słownych (wartości alfanumerycznych w Twoim przypadku) lub bez znaków słownych (+- znaki w danym przypadku).

14

To będzie działać, korzystając itertools.groupby

z = ['z+2-44', '4+55+z+88'] 

print([["".join(x) for k,x in itertools.groupby(i,str.isalnum)] for i in z]) 

wyjściowa:

[['z', '+', '2', '-', '44'], ['4', '+', '55', '+', 'z', '+', '88']] 

To zaledwie kilka z znaki alfanumeryczne jeśli oni (lub nie), po prostu połączyć je z powrotem na liście zrozumieniem.

EDYCJA: ogólny przypadek kalkulatora z nawiasem został zadany jako kolejne pytanie here. Jeśli z jest następujący:

z = ['z+2-44', '4+55+((z+88))'] 

następnie z poprzedniej grupy otrzymujemy:

[['z', '+', '2', '-', '44'], ['4', '+', '55', '+((', 'z', '+', '88', '))']] 

co nie jest łatwe do analizowania pod względem żetonów. Więc zmiana byłaby join tylko jeśli ALPHANUM i niech jak listy, jeśli nie, to spłaszczenie w końcu za pomocą chain.from_iterable:

print([list(itertools.chain.from_iterable(["".join(x)] if k else x for k,x in itertools.groupby(i,str.isalnum))) for i in z]) 

co daje:

[['z', '+', '2', '-', '44'], ['4', '+', '55', '+', '(', '(', 'z', '+', '88', ')', ')']] 

(zauważ, że alternatywny regex odpowiedź może również zostać dostosowany tak: [re.findall('\w+|\W', s) for s in lst] (zauważ brak + po W)

również "".join(list(x)) jest nieco szybciej niż "".join(x), ale Pozwolę ci to dodać, aby uniknąć zmiany widoczności tego już złożonego wyrażenia.

+1

Biłeś mnie przez 3 sekundy: P –

+1

nie jesteś bolesnym przegranym, jak widzę :) dzięki za edycję –

6

rozwiązanie Alternatywa użyciu re.split funkcję:

l = ['z+2-44', '4+55+z+88'] 
print([list(filter(None, re.split(r'(\w+)', i))) for i in l]) 

Wyjście:

[['z', '+', '2', '-', '44'], ['4', '+', '55', '+', 'z', '+', '88']] 
-1

Jeśli chcesz trzymać split (stąd unikanie regex), można podać go z opcjonalnym charakterem podzielić na:

>>> testing = 'z+2-44' 
>>> testing.split('+') 
['z', '2-44'] 
>>> testing.split('-') 
['z+2', '44'] 

Możesz więc poprawić coś c haining rozdzielone polecenia.

Jednakże, używając wyrażeń regularnych będzie prawdopodobnie bardziej czytelne:

import re 

>>> re.split('\+|\-', testing) 
['z', '2', '44'] 

To jest po prostu mówiąc do „podzielić ciąg w dowolnym + lub - charakter” (ukośniki są znaki ewakuacyjne, ponieważ oba te mają specjalny czyli w regex

Wreszcie, w tym konkretnym przypadku, wyobrażam sobie cel jest coś wzdłuż linii „rozłamu w każdym non-alfa znaku numerycznego”, w którym to przypadku regex można jeszcze zapisać dzień.

>>> re.split('[^a-zA-Z0-9]', testing) 
['z', '2', '44'] 

Warto oczywiście zauważyć, że istnieje milion innych rozwiązań, o których mowa w kilku innych dyskusjach na temat SO.

Python: Split string with multiple delimiters

Split Strings with Multiple Delimiters?

tutaj moje odpowiedzi są skierowane w kierunku prostego, czytelnego kodu, a nie wydajność, na cześć Donald Knuth

+1

Pytający chce, aby te znaki znalazły się również na liście wyników. nie tylko z 2 44. – Lafexlos

+0

Ach tak, powinienem przeczytać to pytanie lepiej. Chciałbym zaktualizować odpowiedź, ale widzę, że na tym etapie została już udzielona odpowiedź. Kontynuować! – MrName

4

Można użyć tylko str.replace() i str.split() wbudowanych funkcji W ciągu zrozumienie listy:

In [34]: lst = ['z+2-44', '4+55+z+88'] 

In [35]: [s.replace('+', ' + ').replace('-', ' - ').split() for s in lst] 
Out[35]: [['z', '+', '2', '-', '44'], ['4', '+', '55', '+', 'z', '+', '88']] 

Należy jednak zauważyć, że nie jest to skuteczne podejście do dłuższych łańcuchów. W takim przypadku najlepiej jest użyć wyrażenia regularnego.

Jako inny pythonic sposób można również wykorzystać moduł tokenize:

In [56]: from io import StringIO 

In [57]: import tokenize 

In [59]: [[t.string for t in tokenize.generate_tokens(StringIO(i).readline)][:-1] for i in lst] 
Out[59]: [['z', '+', '2', '-', '44'], ['4', '+', '55', '+', 'z', '+', '88']] 

Moduł tokenize zapewnia leksykalne skanera kodu źródłowego Python, realizowany w Pythonie. Skaner w tym module zwraca również komentarze jako tokeny, dzięki czemu jest przydatny do implementacji "ładnych drukarek", w tym do kolorowania dla wyświetlaczy ekranowych.

Powiązane problemy