Myślę, że to, co chcę zrobić, jest dość powszechnym zadaniem, ale nie znalazłem żadnych odnośników w Internecie. Mam tekst ze znakami interpunkcyjnymi i chcę listę słów.
"Hey, you - what are you doing here!?"
Powinien być
['hey', 'you', 'what', 'are', 'you', 'doing', 'here']
Ale Python str.split()
działa tylko z jednym argumentem, więc mam wszystkie słowa z interpunkcją po podzieleniu spacją. Jakieś pomysły?
str.split()
działa również bez żadnych argumentówOdpowiedzi:
Przypadek, w którym wyrażenia regularne są uzasadnione:
źródło
re
prostu niefindall
. Poniższa odpowiedźre.split()
jest lepsza.don't
jest traktowane jako pojedyncze słowo, a nie w podziale nadon
it
.re.split ()
źródło
\w
,\W
,\s
, i\S
. Ten, kto myślał, że wielkie litery powinny odwracać ich znaczenie, musi zostać postrzelony w głowę.shift
klucza, aby zrobić coś przeciwnego.ctrl+z
cofnij vs.ctrl+shift+z
przywróć. Tak więcshift w
, lubW
byłoby odwrotniew
.Innym szybkim sposobem na zrobienie tego bez wyrażenia regularnego jest zastąpienie znaków, jak poniżej:
źródło
Tak wielu odpowiedzi, ale nie mogę znaleźć żadnego rozwiązania, które by efektywnie spełniało oczekiwania dosłownie tytułu pytań (podział na wiele możliwych separatorów - zamiast tego wiele odpowiedzi dzieli się na wszystko, co nie jest słowem, co jest inne). Oto odpowiedź na pytanie zawarte w tytule, które opiera się na standardowym i wydajnym
re
module Pythona :gdzie:
[…]
wyników jednym z podanych w separatorach,\-
w wyrażeniu regularnym jest tutaj, aby zapobiec szczególną interpretację-
jako wskaźnik zakresu znaków (jak wA-Z
)+
pomija jeden lub więcej ograniczników (może to być pominięte dziękifilter()
, ale to niepotrzebnie produkować puste struny między dopasowanych separatorów), orazfilter(None, …)
usuwa puste ciągi, które mogą zostać utworzone przez separatory wiodące i końcowe (ponieważ puste ciągi mają fałszywą wartość boolowską).To
re.split()
właśnie „dzieli z wieloma separatorami”, co postulowano w tytule pytanie.To rozwiązanie jest ponadto odporne na problemy ze znakami spoza ASCII w słowach znalezionych w niektórych innych rozwiązaniach (patrz pierwszy komentarz do odpowiedzi ghostdog74 ).
re
Moduł jest znacznie bardziej efektywny (w szybkości i zwięzłości) niż robić pętle Python i testy „ręcznie”!źródło
Innym sposobem, bez wyrażenia regularnego
źródło
"Hey, you - what are you doing here María!?"
. Zaakceptowane rozwiązanie nie będzie działać z poprzednim przykładem.''.join([o if not o in string.punctuation else ' ' for o in s]).split()
o for o in s if (o in not string.punctuation or o == "'")
, ale wtedy staje się to zbyt skomplikowane dla jednej linijki, jeśli dodamy również łatkę cedbeu."First Name,Last Name,Street Address,City,State,Zip Code"
i chcemy podzielić tylko przecinek,
. Pożądane wyjście byłoby:['First Name', 'Last Name', 'Street Address', 'City', 'State', 'Zip Code']
Co zamiast tego otrzymujemy:['First', 'Name', 'Last', 'Name', 'Street', 'Address', 'City', 'State', 'Zip', 'Code']
re
moduł jest standardowy i zapewnia zarówno czytelność, jak i szybkość, nie rozumiem, dlaczego należy go unikać.Porada: Użyj
string.translate
do najszybszych operacji na łańcuchach, które ma Python.Jakiś dowód ...
Po pierwsze, droga wolna (przepraszam pprzemek):
Następnie używamy
re.findall()
(zgodnie z sugerowaną odpowiedzią). O wiele szybciej:Wreszcie używamy
translate
:Wyjaśnienie:
string.translate
jest zaimplementowany w C i w przeciwieństwie do wielu funkcji manipulacji ciągiem w Pythonie,string.translate
nie robi tego tworzy nowego ciągu. Jest więc tak szybko, jak to możliwe, aby zastąpić ciąg znaków.Jest to jednak trochę niezręczne, ponieważ do wykonania tej magii potrzebuje tabeli tłumaczeń. Możesz utworzyć tabelę tłumaczeń z
maketrans()
funkcją wygody. Celem jest przetłumaczenie wszystkich niechcianych postaci na spacje. Zastępca jeden do jednego. Ponownie nie są generowane żadne nowe dane. To jest szybkie !Następnie używamy starego dobrego
split()
.split()
domyślnie będzie działać na wszystkich znakach spacji, grupując je razem dla podziału. Rezultatem będzie lista słów, które chcesz. To podejście jest prawie 4x szybsze niżre.findall()
!źródło
patt = re.compile(ur'\w+', re.UNICODE); patt.findall(S)
jest szybsze niż tłumaczenie, ponieważ musisz zakodować ciąg przed zastosowaniem transformacji i zdekodować każdy element na liście po podziale, aby wrócić do Unicode.s.translate(''.join([(chr(i) if chr(i) not in seps else seps[0]) for i in range(256)])).split(seps[0])
Miałem podobny dylemat i nie chciałem używać modułu „re”.
źródło
re
modułu, który jest zarówno o wiele szybszy, jak i wyraźniejszy (nie dlatego, że wyrażenia regularne są szczególnie wyraźne, ale ponieważ są o wiele krótsze i bezpośrednie)?Po pierwsze, chcę zgodzić się z innymi, że regex lub
str.translate(...)
oparte na nim rozwiązania są najbardziej wydajne. W moim przypadku użycie tej funkcji nie było znaczące, dlatego chciałem dodać pomysły, które wziąłem pod uwagę przy tych kryteriach.Moim głównym celem było uogólnienie pomysłów z niektórych innych odpowiedzi w jedno rozwiązanie, które może pracować dla ciągów zawierających więcej niż tylko słowa wyrażenia regularnego (tj. Umieszczenie na czarnej liście wyraźnego podzbioru znaków interpunkcyjnych w porównaniu do znaków słów z białej listy).
Zauważ, że w każdym podejściu można również rozważyć użycie
string.punctuation
zamiast ręcznie zdefiniowanej listy.Opcja 1 - re
Byłem zaskoczony, że do tej pory nie otrzymałem odpowiedzi, używa re.sub (...) . Uważam to za proste i naturalne podejście do tego problemu.
W tym rozwiązaniu zagnieździłem wezwanie do
re.sub(...)
wewnątrzre.split(...)
- ale jeśli wydajność jest krytyczna, kompilacja wyrażenia regularnego na zewnątrz może być korzystna - w moim przypadku różnica nie była znacząca, więc wolę prostotę i czytelność.Opcja 2 - wymiana str
Jest to jeszcze kilka wierszy, ale ma tę zaletę, że można ją rozbudowywać bez konieczności sprawdzania, czy trzeba uciec pewnej postaci w wyrażeniu regularnym.
Byłoby miło móc zamiast tego zamapować str .replace na ciąg, ale nie sądzę, że można tego dokonać za pomocą niezmiennych ciągów, a podczas mapowania na listę znaków zadziałałoby, uruchamiając każdą zamianę na każdym znaku brzmi nadmiernie. (Edycja: zobacz następną opcję dla funkcjonalnego przykładu.)
Opcja 3 - funkools.reduce
(W Python 2
reduce
jest dostępny w globalnej przestrzeni nazw bez importowania go z funools.)źródło
str.translate
- nie jest ona w stanie unicode, ale najprawdopodobniej jest szybsza niż inne metody i jako taka może być dobra w niektórych przypadkach:replacements=',-!?'; import string; my_str = my_str.translate(string.maketrans(replacements, ' ' * len(replacements)))
Również tutaj obowiązkowe jest zastępowanie jako ciąg znaków, a nie krotka lub lista.To staje się trójliniowe:
Wyjaśnienie
To właśnie w Haskell nazywa się monadą List. Idea stojąca za monadą polega na tym, że raz „w monadzie” zostajesz w monadzie, dopóki coś cię nie wyciągnie. Na przykład w Haskell powiedzmy, że mapujesz
range(n) -> [1,2,...,n]
funkcję python na Listę. Jeśli wynikiem jest lista, zostanie ona dołączona do listy w miejscu, aby uzyskać coś takiegomap(range, [3,4,1]) -> [0,1,2,0,1,2,3,0]
. Jest to znane jako map-append (lub mappend, a może coś takiego). Chodzi o to, że masz tę operację, którą stosujesz (dzielenie na token), i za każdym razem, gdy to robisz, dołączasz wynik do listy.Możesz to wydzielić w funkcję i mieć
tokens=string.punctuation
domyślnie.Zalety tego podejścia:
źródło
map_then_append
może być wykorzystana do uczynienia problemu 2-liniowym, a także wielu innych problemów o wiele łatwiejszym do napisania. Większość innych rozwiązań wykorzystujere
moduł wyrażeń regularnych , który nie jest pythonem. Ale byłem niezadowolony z tego, jak sprawiam, że moja odpowiedź wydaje się nieelegancka i rozdęta, kiedy jest naprawdę zwięzła ... Zamierzam ją edytować ...fragments
wynik to tylko lista znaków w ciągu (łącznie z tokenami).fragments = ['the,string']
,fragments = 'the,string'
albofragments = list('the,string')
i żaden z nich nie przynoszą odpowiedniego wyjścia.Spróbuj tego:
to zostanie wydrukowane
['Hey', 'you', 'what', 'are', 'you', 'doing', 'here']
źródło
Użyj zamień dwa razy:
prowadzi do:
źródło
Lubię ponownie , ale oto moje rozwiązanie bez niego:
sep .__ zawiera__ jest metodą stosowaną przez operatora „in”. Zasadniczo jest to to samo co
ale jest tutaj wygodniejszy.
groupby pobiera nasz ciąg i funkcję. Dzieli ciąg na grupy za pomocą tej funkcji: ilekroć zmienia się wartość funkcji - generowana jest nowa grupa. Więc sep .__ zawiera__ jest dokładnie tym, czego potrzebujemy.
groupby zwraca ciąg par, gdzie para [0] jest wynikiem naszej funkcji, a para [1] jest grupą. Używając „jeśli nie k” odfiltrowujemy grupy z separatorami (ponieważ wynik sep .__ zawiera__ jest prawdziwy dla separatorów). Cóż, to wszystko - teraz mamy sekwencję grup, w których każda jest słowem (grupa jest w rzeczywistości iterowalna, więc używamy sprzężenia, aby przekonwertować ją na ciąg znaków).
To rozwiązanie jest dość ogólne, ponieważ wykorzystuje funkcję do oddzielania łańcucha znaków (możesz podzielić według dowolnych warunków). Ponadto nie tworzy pośrednich ciągów / list (możesz usunąć złączenie, a wyrażenie stanie się leniwe, ponieważ każda grupa jest iteratorem)
źródło
Zamiast korzystać z funkcji ponownego modułu re.split, możesz osiągnąć ten sam wynik, stosując metodę pand serii.str.split.
Najpierw utwórz serię z powyższym ciągiem, a następnie zastosuj metodę do serii.
thestring = pd.Series("Hey, you - what are you doing here!?") thestring.str.split(pat = ',|-')
parametr pat pobiera ograniczniki i zwraca podzielony ciąg jako tablicę. Tutaj dwa separatory są przekazywane za pomocą | (lub operator). Dane wyjściowe są następujące:
[Hey, you , what are you doing here!?]
źródło
Ponownie zapoznałem się z Pythonem i potrzebowałem tego samego. Rozwiązanie Findall może być lepsze, ale wpadłem na to:
źródło
używając maketrans i tłumacz, możesz to zrobić łatwo i porządnie
źródło
W Python 3 możesz użyć metody z PY4E - Python for Everybody .
your_string.translate(your_string.maketrans(fromstr, tostr, deletestr))
Możesz zobaczyć „interpunkcję”:
Na przykład:
Aby uzyskać więcej informacji, możesz odwołać się:
źródło
Innym sposobem na osiągnięcie tego jest użycie zestawu Natural Language Tool Kit ( nltk ).
To drukuje:
['Hey', 'you', 'what', 'are', 'you', 'doing', 'here']
Największą wadą tej metody jest to, że musisz zainstalować pakiet nltk .
Zaletą jest to, że po otrzymaniu tokenów możesz zrobić wiele fajnych rzeczy z resztą pakietu nltk.
źródło
Po pierwsze, nie sądzę, że twoim intencją jest używanie interpunkcji jako ograniczników w funkcjach podziału. Twój opis sugeruje, że chcesz po prostu wyeliminować interpunkcję z powstałych ciągów.
Często się z tym spotykam, a moje zwykłe rozwiązanie nie wymaga ponownego.
Jednowierszowa funkcja lambda ze zrozumieniem listy:
(wymaga
import string
):Funkcja (tradycyjna)
Jako tradycyjna funkcja są to tylko dwa wiersze ze zrozumieniem listy (oprócz
import string
):Naturalnie pozostawi również nienaruszone skurcze i dzielone słowa. Zawsze możesz użyć,
text.replace("-", " ")
aby zamienić łączniki w spacje przed podziałem.Funkcja ogólna bez analizy lambda lub listy
Aby uzyskać bardziej ogólne rozwiązanie (w którym można określić znaki do wyeliminowania) i bez zrozumienia listy, otrzymujesz:
Oczywiście zawsze możesz uogólnić funkcję lambda na dowolny określony ciąg znaków.
źródło
Przede wszystkim zawsze używaj re.compile () przed wykonaniem jakiejkolwiek operacji RegEx w pętli, ponieważ działa ona szybciej niż normalna operacja.
więc dla twojego problemu najpierw skompiluj wzór, a następnie wykonaj na nim akcję.
źródło
Oto odpowiedź z wyjaśnieniem.
lub w jednym wierszu możemy to zrobić w następujący sposób:
zaktualizowana odpowiedź
źródło
Utwórz funkcję, która pobiera jako dane wejściowe dwa ciągi (łańcuch źródłowy, który ma zostać podzielony i ciąg listy rozdzielającej ograniczników) i wyświetla listę podzielonych słów:
źródło
Podoba mi się rozwiązanie pprzemek, ponieważ nie zakłada, że ograniczniki są pojedynczymi znakami i nie próbuje używać wyrażenia regularnego (co nie działałoby dobrze, gdyby liczba separatorów musiała być szalona długa).
Oto bardziej czytelna wersja powyższego rozwiązania dla przejrzystości:
źródło
Mam taki sam problem jak @ooboo i uważam, że ten temat @ ghostdog74 zainspirował mnie, może ktoś uzna moje rozwiązanie za przydatne
wprowadź coś w miejscu spacji i podziel za pomocą tego samego znaku, jeśli nie chcesz rozdzielać spacji.
źródło
Oto mój wybór z podziałem z wieloma ogranicznikami:
źródło
Myślę, że najlepszą odpowiedzią na twoje potrzeby jest:
\W+
może być odpowiedni dla tego przypadku, ale może nie być odpowiedni dla innych przypadków.źródło
\w
a\W
rozwiązania nie są odpowiedzią na (tytuł) pytania. Zauważ, że w twojej odpowiedzi|
należy usunąć (expr0|expr1
zamiast tego myślisz[char0 char1…]
). Ponadtocompile()
wyrażenie regularne nie jest potrzebne .Oto moje zdanie na ten temat ....
źródło
replace()
Najbardziej podoba mi się sposób. Poniższa procedura zmienia wszystkie separatory zdefiniowane w ciągusplitlist
na pierwszy separator w,splitlist
a następnie dzieli tekst na tym jednym separatorze. Uwzględnia również, jeślisplitlist
akurat jest to pusty ciąg. Zwraca listę słów, bez pustych ciągów.źródło
Oto użycie:
źródło
Jeśli chcesz operacji odwracalnej (zachowaj ograniczniki), możesz użyć tej funkcji:
źródło
Ostatnio musiałem to zrobić, ale chciałem funkcji, która nieco pasuje do standardowej
str.split
funkcji biblioteki , ta funkcja zachowuje się tak samo jak standardowa biblioteka, gdy jest wywoływana z argumentami 0 lub 1.UWAGA : Ta funkcja jest przydatna tylko wtedy, gdy separatory składają się z jednego znaku (tak jak moja przypadek użycia).
źródło