Dlaczego w wynikach metody split () zwracane są puste ciągi?

120

Jaki jest sens '/segment/segment/'.split('/')powrotu ['', 'segment', 'segment', '']?

Zwróć uwagę na puste elementy. Jeśli dzielisz się na ograniczniku, który znajduje się na pierwszej pozycji i na samym końcu ciągu, jaką dodatkową wartość daje ci to, że pusty ciąg jest zwracany z każdego końca?

orokusaki
źródło
1
Mam to samo pytanie i długo je szukałem. Teraz rozumiem, że puste wyniki są naprawdę ważne. Dziękuję za pytanie.
emeraldhieu
2
Rozwiązaniem jest użycie strip()do usunięcia wiodących i końcowych znaków podziału z ciągu przed podziałem:'/segment/segment/'.strip('/').split('/')
pkamb,

Odpowiedzi:

178

str.splituzupełnienia str.join, tak

"/".join(['', 'segment', 'segment', ''])

przywraca oryginalny ciąg.

Gdyby nie było pustych ciągów, nie '/'byłoby pierwszego i ostatniego pojoin()

John La Rooy
źródło
11
Proste, ale w pełni odpowiada na pytanie.
orokusaki
Byłem zszokowany, gdy odkryłem, że cudzysłowy w zawiasach są rzeczywiście prawidłowe w Pythonie ... ale, ale ... jak? Wydaje się, że doktorzy o tym nie wspominają.
Tim Pietzcker
@Tim mam pojęcia, w jaki sposób te cytaty dostał tam: /
John La Rooy
7
Więc nie używasz Microsoft Word jako swojego Python IDE? :)
Tim Pietzcker
1
@ aaa90210, który powiedział, że proste odpowiedzi nie są najlepsze? Był to komentarz (po raz pierwszy 5 lat temu) o tym, jak odpowiedź była prosta, ale w pełni odpowiadała na pytanie. Użycie „ale” w zdaniu nie oznacza niczego złego. Niełatwa odpowiedź mogłaby być bardziej kompletną odpowiedzią (np. Zawierająca odpowiednie decyzje lub PEP związany z wymienioną funkcjonalnością).
orokusaki
88

Bardziej ogólnie, aby usunąć puste ciągi zwracane w split()wynikach, możesz przyjrzeć się filterfunkcji.

Przykład:

filter(None, '/segment/segment/'.split('/'))

zwroty

['segment', 'segment']
Franck Dernoncourt
źródło
3
Dziękuję za to, nie wiem, dlaczego ta odpowiedź jest tak daleko idąca, wszystko inne jest szczątkowe.
Klin
6
Jeśli chcesz zebrać wynik na liście, zamiast pobierać obiekt filtru jako wyjście, umieść całą strukturę filtru list(...).
Tim Visée
29

Należy wziąć pod uwagę dwie główne kwestie:

  • Oczekiwanie, że wynik '/segment/segment/'.split('/')będzie równy, ['segment', 'segment']jest rozsądne, ale wtedy powoduje to utratę informacji. Jeśli split()działało tak, jak chciałeś, jeśli ci powiem a.split('/') == ['segment', 'segment'], nie możesz mi powiedzieć, co abyło.
  • Jaki powinien być wynik 'a//b'.split()bycia? ['a', 'b']? lub ['a', '', 'b']? Czyli należy split()łączyć sąsiednie ograniczniki? Gdyby tak było, bardzo trudno będzie przeanalizować dane oddzielone znakiem, a niektóre pola mogą być puste. Jestem całkiem pewien, że wielu ludzi, którzy zrobienia chcą pustych wartości w wyniku dla powyższego przypadku!

Ostatecznie sprowadza się to do dwóch rzeczy:

Spójność: jeśli mam nograniczniki, w a, otrzymuję n+1wartości z powrotem po split().

Powinno być możliwe wykonywanie skomplikowanych rzeczy i łatwe do robienia prostych rzeczy: jeśli chcesz zignorować puste ciągi znaków jako wynik split(), zawsze możesz zrobić:

def mysplit(s, delim=None):
    return [x for x in s.split(delim) if x]

ale jeśli ktoś nie chce ignorować pustych wartości, powinien być w stanie.

Język musi wybrać jedną definicję split()- jest zbyt wiele różnych przypadków użycia, aby domyślnie spełnić wymagania wszystkich. Myślę, że wybór Pythona jest dobry i najbardziej logiczny. (Na marginesie, jednym z powodów, dla których nie lubię C jest strtok()to, że łączy sąsiednie ograniczniki, co sprawia, że ​​niezwykle trudno jest przeprowadzić z nim poważną analizę / tokenizację).

Jest jeden wyjątek: a.split()bez argumentu ściska kolejne białe spacje, ale można argumentować, że w takim przypadku jest to właściwe postępowanie. Jeśli nie chcesz takiego zachowania, zawsze możesz to zrobić a.split(' ').

Alok Singhal
źródło
Dla tych, którzy zastanawiają się, czy szybciej jest nuke zduplikować spacje, a następnie podzielić, czy też podzielić i wziąć tylko niepuste ciągi, oto co otrzymuję: python3 -m timeit "import re ; re.sub(' +', ' foo bar baz ', '').split(' ')"-> 875 nsec na pętlę; python3 -m timeit "[token for token in ' foo bar baz '.split(' ') if token]"-> 616 nsec na pętlę
s3cur3
8

Mając x.split(y)zawsze zwróci listę 1 + x.count(y)elementów jest cennym regularność - jak @ gnibbler już zauważył to sprawia spliti joindokładne odwrotności siebie (jak oczywiście powinny być), ale również precyzyjnie odwzorowuje semantykę wszystkich rodzajów płyt separatorów mikrowczepy ( takie jak csvwiersze pliku [[bez cudzysłowów]], wiersze z /etc/groupsystemu Unix i tak dalej), umożliwia (jak wspomniano w odpowiedzi @ Romana) łatwe sprawdzenie (np.) ścieżek bezwzględnych i względnych (w ścieżkach plików i adresach URL), i tak dalej.

Innym sposobem spojrzenia na to jest to, że nie powinieneś bezmyślnie wyrzucać informacji przez okno bez żadnych korzyści. Co można by zyskać, gdybyśmy byli x.split(y)równoważni x.strip(y).split(y)? Nic, oczywiście - jest łatwy w użyciu drugą formę, gdy to, co masz na myśli, ale jeśli pierwsza forma została arbitralnie uznane oznaczać drugi, to że mają wiele do zrobienia, jeśli nie chcą pierwsza ( co nie jest rzadkie, jak wskazano w poprzednim akapicie).

Ale tak naprawdę myślenie w kategoriach matematycznej regularności jest najprostszym i najbardziej ogólnym sposobem, w jaki możesz nauczyć się projektować zadowalające API. Aby wziąć inny przykład, bardzo ważne jest, aby dla każdego ważnego xi y x == x[:y] + x[y:]- co natychmiast wskazuje, dlaczego należy wykluczyć jedną skrajność krojenia . Im prostsze niezmienne twierdzenie możesz sformułować, tym bardziej prawdopodobne jest, że wynikająca z niego semantyka jest tym, czego potrzebujesz w prawdziwym życiu - część mistycznego faktu, że matematyka jest bardzo przydatna w radzeniu sobie ze wszechświatem.

Spróbuj sformułować niezmiennik dla splitdialektu, w którym początkowe i końcowe ograniczniki są umieszczone w specjalnej wielkości liter ... kontrprzykład: metody łańcuchowe, takie jak isspacenie są maksymalnie proste - x.isspace()jest równoważne x and all(c in string.whitespace for c in x)- to głupie prowadzenie x andjest powodem, dla którego tak często znajdujesz się w kodowaniu not x or x.isspace(), aby powrócić do prostoty, która powinna zostać zaprojektowana w is...metodach łańcuchowych (gdzie pusty łańcuch "jest" wszystkim, czego chcesz - w przeciwieństwie do wyczucia konia na ulicy, może [[puste zestawy, takie jak zero & c, zawsze myliły większość ludzi ;-)]], ale w pełni zgodne z oczywistym, dobrze wyrafinowanym zdrowym rozsądkiem matematycznym ! -).

Alex Martelli
źródło
5

Nie wiem, jakiej odpowiedzi szukasz? Otrzymujesz trzy dopasowania, ponieważ masz trzy ograniczniki. Jeśli nie chcesz tego pustego, po prostu użyj:

'/segment/segment/'.strip('/').split('/')
jamieb
źródło
4
-1, ponieważ otrzymujesz cztery dopasowania, a nie trzy, a to również nie odpowiada na pytanie.
Roman
1
+1, aby przeciwdziałać negowi .. Nie powiedział, że otrzymasz trzy wyniki z powrotem. Powiedział „trzy dopasowania” zamiast „trzech separatorów”, co dla mnie brzmi logicznie. Nie otrzymujesz jednak „czterech dopasowań” niczego. Jednak w wyniku zwracane są „cztery elementy”. Nie odpowiada też bezpośrednio na pytanie „dlaczego”, ale zapewnia prosty sposób na uzyskanie tego, czego naprawdę chce ... co nie zasługuje na złą opinię. Jeśli zamierzasz kogoś oszukać (nie mniejszym głosem), bądź ostrożny! Twoje zdrowie! 8 ^)
kodybrown,
@wasatchwizard Dzięki za wyjaśnienie. Doceniam korektę i rekomendację. Niestety, teraz mój głos jest zablokowany i nie można go zmienić.
Roman
Uwielbiam twoje rozwiązanie - rozebrać, a następnie podzielić, aby usunąć pusty wynik
Nam G VU
5

Cóż, pozwala ci wiedzieć, że był tam separator. Tak więc wyświetlenie 4 wyników pozwala zorientować się, że masz 3 ograniczniki. Dzięki temu możesz robić, co chcesz z tymi informacjami, zamiast zmuszać Pythona do usuwania pustych elementów, a następnie zmuszać Cię do ręcznego sprawdzania początkowych lub końcowych ograniczników, jeśli chcesz to wiedzieć.

Prosty przykład: załóżmy, że chcesz sprawdzić bezwzględne i względne nazwy plików. W ten sposób możesz to wszystko zrobić z podziałem, bez konieczności sprawdzania pierwszego znaku nazwy pliku.

rzymski
źródło
1

Rozważ ten minimalny przykład:

>>> '/'.split('/')
['', '']

splitmusi podać to, co znajduje się przed i za separatorem '/', ale nie ma innych znaków. Więc to ma dać wam pusty ciąg znaków, który technicznie poprzedza i następuje '/', ponieważ '' + '/' + '' == '/'.

timgeb
źródło