Ketika memisahkan string kosong dengan Python, mengapa split () mengembalikan daftar kosong sementara split ('\ n') mengembalikan ['']?

154

Saya menggunakan split('\n')untuk mendapatkan baris dalam satu string, dan menemukan bahwa ''.split()mengembalikan daftar kosong [],, sambil ''.split('\n')mengembalikan ['']. Apakah ada alasan khusus untuk perbedaan seperti itu?

Dan apakah ada cara yang lebih mudah untuk menghitung garis dalam sebuah string?

godice
sumber
1
Kemungkinan rangkap dari Mengapa string kosong dikembalikan dalam hasil split ()?
Ioannis Filippidis

Jawaban:

247

Pertanyaan: Saya menggunakan split ('\ n') untuk mendapatkan baris dalam satu string, dan menemukan bahwa '' .split () mengembalikan daftar kosong [], sedangkan '' .split ('\ n') mengembalikan [''] .

Metode str.split () memiliki dua algoritma. Jika tidak ada argumen yang diberikan, itu terbagi pada menjalankan spasi putih berulang. Namun, jika argumen diberikan, itu diperlakukan sebagai pembatas tunggal tanpa berjalan berulang.

Dalam kasus pemisahan string kosong, mode pertama (tanpa argumen) akan mengembalikan daftar kosong karena spasi kosong dimakan dan tidak ada nilai untuk dimasukkan ke dalam daftar hasil.

Sebaliknya, mode kedua (dengan argumen seperti \n) akan menghasilkan bidang kosong pertama. Pertimbangkan jika Anda telah menulis '\n'.split('\n'), Anda akan mendapatkan dua bidang (satu split, memberi Anda dua bagian).

Pertanyaan: Apakah ada alasan khusus untuk perbedaan seperti itu?

Mode pertama ini berguna ketika data disejajarkan dalam kolom dengan jumlah spasi kosong yang bervariasi. Sebagai contoh:

>>> 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']

Mode kedua berguna untuk data yang dibatasi seperti CSV di mana koma berulang menunjukkan bidang kosong. Sebagai contoh:

>>> 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']

Catatan, jumlah bidang hasil adalah satu lebih besar dari jumlah pembatas. Pikirkan memotong tali. Jika Anda tidak memotong, Anda memiliki satu potong. Membuat satu potong, beri dua potong. Membuat dua potongan, beri tiga potong. Dan demikian pula dengan str.split (pembatas) Python metode :

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

Pertanyaan: Dan apakah ada cara yang lebih mudah untuk menghitung garis dalam sebuah string?

Ya, ada beberapa cara mudah. Satu menggunakan str.count () dan yang lainnya menggunakan str.splitlines () . Kedua cara akan memberikan jawaban yang sama kecuali jika baris terakhir tidak ada \n. Jika baris akhir terakhir tidak ada, pendekatan str.splitlines akan memberikan jawaban yang akurat. Teknik yang lebih cepat yang juga akurat menggunakan metode penghitungan tetapi kemudian memperbaikinya untuk baris baru terakhir:

>>> 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    

Pertanyaan dari @Kaz: Kenapa sih dua algoritma yang sangat berbeda bertanduk sepatu menjadi satu fungsi?

Tanda tangan untuk str.split berusia sekitar 20 tahun, dan sejumlah API dari era itu sangat pragmatis. Meskipun tidak sempurna, metode tanda tangan juga tidak "mengerikan". Sebagian besar, pilihan desain API Guido telah teruji oleh waktu.

API saat ini bukan tanpa kelebihan. Pertimbangkan string seperti:

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

Ketika diminta untuk memecah string ini menjadi bidang, orang cenderung menggambarkan keduanya menggunakan kata bahasa Inggris yang sama, "split". Ketika diminta membaca kode seperti fields = line.split() atau fields = line.split(','), orang cenderung menafsirkan pernyataan dengan benar sebagai "membagi baris menjadi bidang".

Alat teks-ke-kolom Microsoft Excel membuat pilihan API yang serupa dan menggabungkan kedua algoritma pemisahan dalam alat yang sama. Orang-orang tampaknya secara mental memodelkan field-splitting sebagai konsep tunggal meskipun lebih dari satu algoritma yang terlibat.

Raymond Hettinger
sumber
28

Sepertinya itu hanya cara yang seharusnya berfungsi, menurut dokumentasi :

Memisahkan string kosong dengan pemisah yang ditentukan akan kembali [''].

Jika sep tidak ditentukan atau Tidak, algoritma pemisahan yang berbeda diterapkan: menjalankan spasi kosong berturut-turut dianggap sebagai pemisah tunggal, dan hasilnya tidak akan berisi string kosong di awal atau akhir jika string memiliki spasi spasi awal atau akhir. Akibatnya, memisahkan string kosong atau string yang hanya terdiri dari spasi kosong dengan pemisah None [].

Jadi, untuk membuatnya lebih jelas, split()fungsi mengimplementasikan dua algoritma pemisahan yang berbeda, dan menggunakan keberadaan argumen untuk memutuskan mana yang akan dijalankan. Ini mungkin karena memungkinkan mengoptimalkan yang tanpa argumen lebih dari yang dengan argumen; Saya tidak tahu

beristirahat
sumber
4

.split()tanpa parameter mencoba menjadi pintar. Itu terbagi pada spasi putih, tab, spasi, umpan baris dll, dan juga melewatkan semua string kosong sebagai akibat dari ini.

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

Pada dasarnya, .split()tanpa parameter digunakan untuk mengekstrak kata-kata dari string, berbeda .split()dengan parameter yang hanya mengambil string dan membaginya.

Itulah alasan perbedaannya.

Dan ya, menghitung garis dengan memisahkan bukanlah cara yang efisien. Hitung jumlah umpan baris, dan tambahkan satu jika string tidak diakhiri dengan umpan baris.

Lennart Regebro
sumber
2

Gunakan count():

s = "Line 1\nLine2\nLine3"
n_lines = s.count('\n') + 1
Gareth Webber
sumber
4
+1 hanya boleh dilakukan jika teks tidak diakhiri dengan '\ n'.
Lennart Regebro
8
Nah, jika diakhiri dengan "\ n" maka baris terakhir adalah baris kosong. Meskipun tidak berguna, itu masih dianggap sebagai garis, bukan?
Jakub M.
2
tidak. ketika saya menulis 3 baris teks ke file dan mengakhiri masing-masing dengan linefeed, maka saya akan mengatakan file tersebut berisi 3 baris. pada unix itu adalah praktik terbaik untuk memiliki file teks selalu diakhiri dengan linefeed. jika tidak, cat fileperintah baris dan subversi Anda akan dikeluhkan. vi selalu menambahkan satu.
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.

Perhatikan kalimat terakhir.

Untuk menghitung garis, Anda cukup menghitung berapa banyak \nyang ada:

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

Bagian terakhir memperhitungkan baris terakhir yang tidak berakhir \n, meskipun ini berarti Hello, World!dan Hello, World!\nmemiliki jumlah baris yang sama (yang bagi saya masuk akal), jika tidak, Anda dapat menambahkan 1ke dalam hitungan \n.

Bakuriu
sumber
0

Untuk menghitung garis, Anda dapat menghitung jumlah jeda baris:

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

Edit :

Jawaban lain dengan built-in countlebih cocok, sebenarnya

Jakub M.
sumber
3
Selain hanya menggunakan count, bools dapat ditambahkan (pada kenyataannya, mereka subkelas int), sehingga genexp dapat ditulis sebagai sum(s == "\n" for s in the_string).
Lvc
Saat ini Anda hanya menghitung garis kosong?
Thijs van Dien
Ya, saya tidak membuang garis kosong
Jakub M.