Mam ciąg, który jest taki:
this is "a test"
Próbuję napisać coś w Pythonie, aby podzielić to na spację, ignorując spacje w cudzysłowie. Wynik, którego szukam to:
['this','is','a test']
PS. Wiem, że zapytasz „co się stanie, jeśli w cytatach są cytaty, cóż, w mojej aplikacji, to się nigdy nie wydarzy.
Odpowiedzi:
Chcesz
split
z wbudowanegoshlex
modułu.To powinno zrobić dokładnie to, co chcesz.
źródło
shlex.split('this is "a test"', posix=False)
zwraca['this', 'is', '"a test"']
shlex.split()
spowoduje wyzwolenieUnicodeEncodeError
wyjątku.W szczególności spójrz na
shlex
modułshlex.split
.źródło
Widzę tutaj wyrażenia regularne, które wyglądają na złożone i / lub niepoprawne. Zaskakuje mnie to, ponieważ składnia wyrażeń regularnych może łatwo opisać „białe znaki lub cytaty otoczone rzeczami”, a większość silników wyrażeń regularnych (w tym Pythona) może się dzielić na wyrażenia regularne. Jeśli więc zamierzasz używać wyrażeń regularnych, dlaczego nie powiesz dokładnie, co masz na myśli ?:
Wyjaśnienie:
shlex prawdopodobnie zapewnia jednak więcej funkcji.
źródło
W zależności od przypadku użycia możesz również sprawdzić
csv
moduł:Wynik:
źródło
""
), aby przedstawić jeden podwójny cytat"
, więc zamieni dwa podwójne cudzysłowy w pojedynczy cytat'this is "a string""'
i'this is "a string"""'
oba['this', 'is', 'a string"']
Używam shlex.split do przetworzenia 70 000 000 linii dziennika kałamarnic, to jest tak wolne. Więc przełączyłem się na re.
Spróbuj tego, jeśli masz problem z wydajnością shlex.
źródło
Ponieważ to pytanie jest oznaczone wyrażeniem regularnym, postanowiłem wypróbować podejście wyrażenia regularnego. Najpierw zamieniam wszystkie spacje w częściach cudzysłowów na \ x00, następnie dzielę spacje, a następnie zamieniam \ x00 z powrotem na spacje w każdej części.
Obie wersje robią to samo, ale splitter jest nieco bardziej czytelny niż splitter2.
źródło
Wydaje się, że ze względu na wydajność
re
jest szybszy. Oto moje rozwiązanie wykorzystujące najmniej chciwy operator, który zachowuje zewnętrzne cytaty:Wynik:
Pozostawia konstrukty jak
aaa"bla blub"bbb
razem, ponieważ te tokeny nie są oddzielone spacjami. Jeśli ciąg zawiera znaki specjalne, możesz dopasować w następujący sposób:Należy pamiętać, że to również pasuje do pustego łańcucha
""
za pomocą\S
części wzoru.źródło
,
Via'(?:".*?"|[^,])+'
). To samo dotyczy cudzysłowu (otaczającego) znaku (znaków).Główny problem z przyjętym
shlex
podejściem polega na tym, że nie ignoruje znaków specjalnych poza podanymi podciągami i daje nieco nieoczekiwane wyniki w niektórych przypadkach narożnych.Mam następujący przypadek użycia, w którym potrzebuję funkcji podziału, która dzieli ciągi wejściowe w taki sposób, że zachowane są zarówno jedno-, jak i podwójnie cudzysłowy, z możliwością ucieczki cudzysłowów w takim podciągu. Cytaty w ciągu niecytowanym nie powinny być traktowane inaczej niż jakikolwiek inny znak. Niektóre przykładowe przypadki testowe z oczekiwanym wynikiem:
Skończyłem z następującą funkcją, aby podzielić ciąg tak, aby oczekiwane wyniki wyjściowe dla wszystkich ciągów wejściowych:
Następująca aplikacja testowa sprawdza wyniki innych podejść (
shlex
icsv
na razie) i implementacji niestandardowego podziału:Wynik:
Wydajność jest więc znacznie lepsza
shlex
i może być dalej poprawiana przez prekompilację wyrażenia regularnego, w którym to przypadku przewyższy tocsv
podejście.źródło
shlex
nie zachowuję się zgodnie z oczekiwaniami dla moich przypadków użycia.Aby zachować cytaty, użyj tej funkcji:
źródło
Test szybkości różnych odpowiedzi:
źródło
Hmm, nie mogę znaleźć przycisku „Odpowiedz”… w każdym razie ta odpowiedź jest oparta na podejściu Kate, ale poprawnie dzieli ciągi znaków na podciągi zawierające cytaty, a także usuwa początkowe i końcowe cytowania podciągów:
Działa to na ciągach takich jak
'This is " a \\\"test\\\"\\\'s substring"'
(szalone znaczniki są niestety konieczne, aby powstrzymać Pythona przed usunięciem ucieczki).Jeśli wynikowe znaki ucieczki w łańcuchach na zwróconej liście nie są potrzebne, możesz użyć tej nieco zmienionej wersji funkcji:
źródło
Aby obejść problemy z Unicode w niektórych wersjach Python 2, sugeruję:
źródło
split = lambda a: [b.decode('utf-8') for b in _split(a)]
przeciwnym razie otrzymasz:UnicodeDecodeError: 'ascii' codec can't decode byte ... in position ...: ordinal not in range(128)
Opcjonalnie wypróbuj tssplit:
źródło
Sugeruję:
ciąg testowy:
przechwytywać także „” i „”:
wynik:
aby zignorować puste „” i „”:
wynik:
źródło
re.findall("(?:\".*?\"|'.*?'|[^\s'\"]+)", s)
również napisane .Jeśli nie zależy ci na napisach niż na prostym
Występ:
Lub moduł ciągów
Wydajność: moduł ciągów wydaje się działać lepiej niż metody ciągów
Lub możesz użyć silnika RE
Występ
W przypadku bardzo długich łańcuchów nie należy ładować całego łańcucha do pamięci, a zamiast tego albo podzielić linie, albo użyć pętli iteracyjnej
źródło
Spróbuj tego:
Niektóre ciągi testowe:
źródło
adamsplit("This is 'a test'")
→['This', 'is', "'a", "test'"]