Dlaczego podczas dzielenia pustego ciągu w Pythonie split () zwraca pustą listę, podczas gdy split ('\ n') zwraca ['']?

155

Używam, split('\n')aby uzyskać wiersze w jednym ciągu i okazało się, że ''.split()zwraca pustą listę [], a ''.split('\n')zwraca ['']. Czy jest jakiś konkretny powód takiej różnicy?

Czy istnieje wygodniejszy sposób liczenia wierszy w ciągu?

godice
źródło
1
Możliwy duplikat Dlaczego w wynikach
Ioannis Filippidis

Odpowiedzi:

247

Pytanie: Używam split ('\ n'), aby uzyskać wiersze w jednym ciągu i stwierdziłem, że '' .split () zwraca pustą listę [], podczas gdy '' .split ('\ n') zwraca [''] .

Metoda str.split () ma dwa algorytmy. Jeśli nie podano żadnych argumentów, dzieli się na powtarzające się ciągi białych znaków. Jeśli jednak podano argument, jest on traktowany jako pojedynczy ogranicznik bez powtarzających się przebiegów.

W przypadku dzielenia pustego łańcucha, pierwszy tryb (bez argumentu) zwróci pustą listę, ponieważ biały znak jest zjadany i nie ma żadnych wartości do umieszczenia na liście wyników.

Natomiast drugi tryb (z argumentem takim jak \n) da pierwsze puste pole. Zastanów się, czy napisałeś '\n'.split('\n'), dostałbyś dwa pola (jeden podział daje dwie połowy).

Pytanie: Czy jest jakiś konkretny powód takiej różnicy?

Ten pierwszy tryb jest przydatny, gdy dane są wyrównane w kolumnach ze zmiennymi ilościami białych znaków. Na przykład:

>>> data = '''\
Shasta      California     14,200
McKinley    Alaska         20,300
Fuji        Japan          12,400
'''
>>> for line in data.splitlines():
        print line.split()

['Shasta', 'California', '14,200']
['McKinley', 'Alaska', '20,300']
['Fuji', 'Japan', '12,400']

Drugi tryb jest przydatny w przypadku danych rozdzielanych, takich jak CSV, gdzie powtarzające się przecinki oznaczają puste pola. Na przykład:

>>> data = '''\
Guido,BDFL,,Amsterdam
Barry,FLUFL,,USA
Tim,,,USA
'''
>>> for line in data.splitlines():
        print line.split(',')

['Guido', 'BDFL', '', 'Amsterdam']
['Barry', 'FLUFL', '', 'USA']
['Tim', '', '', 'USA']

Zauważ, że liczba pól wynikowych jest o jeden większa niż liczba ograniczników. Pomyśl o przecięciu liny. Jeśli nie wykonasz żadnych cięć, masz jeden kawałek. Wykonanie jednego cięcia daje dwie sztuki. Wykonanie dwóch cięć daje trzy sztuki. I tak jest z metodą str.split (delimiter) Pythona :

>>> ''.split(',')       # No cuts
['']
>>> ','.split(',')      # One cut
['', '']
>>> ',,'.split(',')     # Two cuts
['', '', '']

Pytanie: Czy istnieje wygodniejszy sposób liczenia wierszy w ciągu?

Tak, jest kilka łatwych sposobów. Jedna używa str.count (), a druga używa str.splitlines () . Oba sposoby dadzą tę samą odpowiedź, chyba że w ostatnim wierszu brakuje \n. Jeśli brakuje ostatniej nowej linii, podejście str.splitlines da dokładną odpowiedź. Szybsza technika, która jest również dokładna, wykorzystuje metodę zliczania, ale następnie koryguje ją pod kątem końcowego znaku nowej linii:

>>> data = '''\
Line 1
Line 2
Line 3
Line 4'''

>>> data.count('\n')                               # Inaccurate
3
>>> len(data.splitlines())                         # Accurate, but slow
4
>>> data.count('\n') + (not data.endswith('\n'))   # Accurate and fast
4    

Pytanie od @Kaz: Dlaczego do cholery są dwa bardzo różne algorytmy połączone w jedną funkcję?

Podpis dla str.split ma około 20 lat, a wiele interfejsów API z tamtej epoki jest ściśle pragmatycznych. Chociaż nie jest doskonały, sygnatura metody nie jest też „straszna”. W większości wybory projektowe API Guido przetrwały próbę czasu.

Obecne API nie jest pozbawione zalet. Rozważ ciągi, takie jak:

ps_aux_header  = "USER               PID  %CPU %MEM      VSZ"
patient_header = "name,age,height,weight"

Poproszeni o podzielenie tych ciągów na pola, ludzie mają tendencję do opisywania obu za pomocą tego samego angielskiego słowa „podziel”. Poproszeni o odczytanie kodu, takiego jak fields = line.split() lub fields = line.split(','), ludzie mają tendencję do poprawnej interpretacji instrukcji jako „dzielenia wiersza na pola”.

Narzędzie zamiany tekstu na kolumny programu Microsoft Excel dokonało podobnego wyboru interfejsu API i zawiera oba algorytmy dzielenia w tym samym narzędziu. Wydaje się, że ludzie modelują w myślach podział pól jako jedną koncepcję, mimo że w grę wchodzi więcej niż jeden algorytm.

Raymond Hettinger
źródło
28

Wygląda na to, że po prostu ma działać, zgodnie z dokumentacją :

Dzielenie pustego ciągu z określonym separatorem zwraca [''].

Jeśli sep nie jest określony lub ma wartość None, stosowany jest inny algorytm podziału: ciągi kolejnych białych znaków są traktowane jako pojedynczy separator, a wynik nie będzie zawierał pustych ciągów na początku lub na końcu, jeśli łańcuch ma początkowe lub końcowe białe spacje. W konsekwencji dzielenie pustego łańcucha lub łańcucha składającego się tylko z białych znaków z separatorem None zwraca [].

Tak więc, aby było jaśniej, split()funkcja implementuje dwa różne algorytmy dzielenia i wykorzystuje obecność argumentu, aby zdecydować, który z nich uruchomić. Może to być spowodowane tym, że pozwala zoptymalizować ten, który nie zawiera argumentów więcej niż ten z argumentami; Nie wiem

rozwijać
źródło
4

.split()bez parametrów stara się być sprytny. Dzieli się na dowolne białe spacje, tabulatory, spacje, nowe wiersze itp., A także w wyniku tego pomija wszystkie puste ciągi.

>>> "  fii    fbar \n bopp ".split()
['fii', 'fbar', 'bopp']

Zasadniczo .split()bez parametrów są używane do wyodrębniania słów z ciągu, w przeciwieństwie do .split()parametrów, które po prostu pobierają ciąg i dzielą go.

To jest powód tej różnicy.

I tak, liczenie linii przez dzielenie nie jest wydajnym sposobem. Policz liczbę wysuwów wiersza i dodaj jeden, jeśli ciąg nie kończy się wysuwem wiersza.

Lennart Regebro
źródło
2

Zastosowanie count():

s = "Line 1\nLine2\nLine3"
n_lines = s.count('\n') + 1
Gareth Webber
źródło
4
+ 1 należy wykonać tylko wtedy, gdy tekst nie kończy się na „\ n”.
Lennart Regebro
8
Cóż, jeśli kończy się na „\ n”, ostatnia linia jest pusta. Chociaż bezużyteczne, nadal liczy się jako linia, prawda?
Jakub M.
2
Nie. kiedy piszę 3 linie tekstu do pliku i kończę każdy z nich znakiem nowej linii, wtedy powiedziałbym, że plik zawiera 3 linie. w systemie UNIX najlepszą praktyką jest, aby plik tekstowy zawsze kończył się znakiem nowej linii. w przeciwnym razie cat filezniekształca linię poleceń i narzeka subversion. vi zawsze dołącza jeden.
user829755
2
>>> print str.split.__doc__
S.split([sep [,maxsplit]]) -> list of strings

Return a list of the words in the string S, using sep as the
delimiter string.  If maxsplit is given, at most maxsplit
splits are done. If sep is not specified or is None, any
whitespace string is a separator and empty strings are removed
from the result.

Zwróć uwagę na ostatnie zdanie.

Aby policzyć linie, możesz po prostu policzyć, ile \nich jest:

line_count = some_string.count('\n') + some_string[-1] != '\n'

Ostatnia część bierze pod uwagę ostatnią linię, która nie kończy się na \n, mimo że oznacza to Hello, World!i Hello, World!\nmają taką samą liczbę linii (co dla mnie jest rozsądne), w przeciwnym razie możesz po prostu dodać 1do liczby \n.

Bakuriu
źródło
0

Aby policzyć wiersze, możesz policzyć podziały wierszy:

n_lines = sum(1 for s in the_string if s == "\n") + 1 # add 1 for last line

Edycja :

countWłaściwie druga odpowiedź z wbudowanym jest bardziej odpowiednia

Jakub M.
źródło
3
Oprócz samego używania count, bools są dodawane (w rzeczywistości są podklasą int), więc genexp można zapisać jako sum(s == "\n" for s in the_string).
lvc
W tej chwili liczysz tylko puste wiersze?
Thijs van Dien
Tak, nie odrzucam żadnych pustych wierszy
Jakub M.