Mengapa string kosong dikembalikan dalam hasil split ()?

120

Apa gunanya '/segment/segment/'.split('/')kembali ['', 'segment', 'segment', '']?

Perhatikan elemen kosong. Jika Anda memisahkan pada pembatas yang kebetulan berada di posisi satu dan di ujung paling akhir string, nilai ekstra apa yang diberikannya agar string kosong dikembalikan dari setiap ujungnya?

orokusaki
sumber
1
Saya memiliki pertanyaan yang sama dan mencarinya untuk waktu yang lama. Sekarang saya mengerti bahwa hasil kosong sangat penting. Terima kasih atas pertanyaan Anda.
emeraldhieu
2
Solusinya adalah dengan menggunakan strip()untuk menghapus karakter yang dipisahkan di depan dan di belakangnya dari string sebelum memisahkan:'/segment/segment/'.strip('/').split('/')
pkamb

Jawaban:

178

str.splitmelengkapi str.join, jadi

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

mendapatkan Anda kembali string aslinya.

Jika string kosong tidak ada, string pertama dan terakhir '/'akan hilang setelahjoin()

John La Rooy
sumber
11
Sederhana, tetapi menjawab pertanyaan sepenuhnya.
orokusaki
Saya terkejut menemukan bahwa tanda kutip keriting sebenarnya valid di Python ... tapi, tapi ... bagaimana? Dokumen tampaknya tidak menyebutkan ini.
Tim Pietzcker
@ Tim, saya tidak tahu bagaimana kutipan itu masuk ke sana: /
John La Rooy
7
Jadi, Anda tidak menggunakan Microsoft Word sebagai IDE Python Anda? :)
Tim Pietzcker
1
@ aaa90210 siapa bilang jawaban sederhana bukan yang terbaik? Itu adalah komentar (pertama, 5 tahun yang lalu) tentang bagaimana jawabannya sederhana, tetapi menjawab pertanyaan sepenuhnya. Menggunakan "tetapi" dalam kalimat tidak menyiratkan sesuatu yang buruk. Jawaban yang tidak sederhana mungkin merupakan jawaban yang lebih lengkap (misalnya, termasuk keputusan yang relevan atau PEP yang terkait dengan fungsi yang dicatat).
orokusaki
88

Secara lebih umum, untuk menghapus string kosong yang dikembalikan dalam split()hasil, Anda mungkin ingin melihat filterfungsinya.

Contoh:

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

kembali

['segment', 'segment']
Franck Dernoncourt
sumber
3
Terima kasih untuk ini, saya tidak tahu mengapa jawaban ini sangat jauh, yang lainnya adalah hal-hal yang belum sempurna.
Wedge
6
Jika diinginkan untuk mengumpulkan hasil dalam daftar alih-alih mendapatkan objek filter sebagai keluaran, masukkan seluruh struktur filter list(...).
Tim Visée
29

Ada dua hal utama yang perlu dipertimbangkan di sini:

  • Mengharapkan hasil '/segment/segment/'.split('/')menjadi sama dengan ['segment', 'segment']adalah wajar, tetapi kemudian ini kehilangan informasi. Jika split()bekerja seperti yang Anda inginkan, jika saya memberi tahu Anda itu a.split('/') == ['segment', 'segment'], Anda tidak dapat memberi tahu saya apa yang aterjadi.
  • Apa yang seharusnya menjadi hasil 'a//b'.split()? ['a', 'b']?, atau ['a', '', 'b']? Yaitu, harus split()menggabungkan pembatas yang berdekatan? Jika harus, maka akan sangat sulit untuk mengurai data yang dibatasi oleh karakter, dan beberapa bidang bisa kosong. Saya cukup yakin ada banyak orang yang melakukan menginginkan nilai-nilai yang kosong dalam hasil untuk kasus di atas!

Pada akhirnya, itu bermuara pada dua hal:

Konsistensi: jika saya memiliki npembatas, di a, saya mendapatkan n+1nilai kembali setelah split().

Seharusnya mungkin untuk melakukan hal-hal kompleks, dan mudah untuk melakukan hal-hal sederhana: jika Anda ingin mengabaikan string kosong sebagai akibat dari split(), Anda selalu dapat melakukan:

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

tetapi jika seseorang tidak ingin mengabaikan nilai kosong, Anda harus melakukannya bisa.

Bahasa harus memilih satu definisi split()— ada terlalu banyak kasus penggunaan yang berbeda untuk memenuhi persyaratan setiap orang sebagai default. Saya pikir pilihan Python itu bagus, dan paling logis. (Sebagai tambahan, salah satu alasan saya tidak suka C.strtok() adalah karena ini menggabungkan pembatas yang berdekatan, membuatnya sangat sulit untuk melakukan parsing / tokenisasi yang serius dengannya.)

Ada satu pengecualian: a.split()tanpa argumen menekan ruang kosong yang berurutan, tetapi orang dapat berargumen bahwa ini adalah hal yang benar untuk dilakukan dalam kasus itu. Jika Anda tidak menginginkan perilaku tersebut, Anda selalu bisa a.split(' ').

Alok Singhal
sumber
Bagi mereka yang bertanya-tanya apakah lebih cepat untuk membuang duplikat spasi, lalu membagi, atau untuk membagi dan hanya mengambil string yang tidak kosong, inilah yang saya dapatkan: python3 -m timeit "import re ; re.sub(' +', ' foo bar baz ', '').split(' ')"-> 875 nsec per loop; python3 -m timeit "[token for token in ' foo bar baz '.split(' ') if token]"-> 616 nsec per loop
s3cur3
8

Memiliki x.split(y)selalu kembali daftar 1 + x.count(y)item adalah keteraturan berharga - sebagai @ gnibbler sudah menunjukkan itu membuat splitdan joininvers yang tepat dari satu sama lain (karena mereka jelas harus), itu juga justru peta semantik semua jenis catatan pembatas-bergabung ( seperti csvbaris file [[masalah kutipan bersih]], baris dari /etc/groupdalam Unix, dan seterusnya), ini memungkinkan (seperti jawaban @ Roman disebutkan) pemeriksaan mudah untuk (misalnya) jalur absolut vs relatif (di jalur file dan URL), Dan seterusnya.

Cara lain untuk melihatnya adalah bahwa Anda tidak boleh sembarangan membuang informasi dari jendela tanpa keuntungan. Apa yang akan diperoleh dengan membuat x.split(y)setara x.strip(y).split(y)? Tidak ada, tentu saja - sangat mudah untuk menggunakan bentuk kedua ketika itu ini apa yang Anda maksud, tetapi jika bentuk pertama sewenang-wenang dianggap berarti yang kedua, Anda akan memiliki banyak pekerjaan yang harus dilakukan ketika Anda tidak ingin yang pertama ( yang jauh dari langka, seperti yang ditunjukkan paragraf sebelumnya).

Tapi sungguh, berpikir dalam istilah keteraturan matematis adalah cara paling sederhana dan paling umum yang bisa Anda pelajari sendiri untuk merancang API yang lumayan. Untuk mengambil contoh yang berbeda, sangat penting bahwa untuk setiap yang valid xdan y x == x[:y] + x[y:]- yang segera menunjukkan mengapa salah satu pemotongan harus dikecualikan. Semakin sederhana pernyataan invarian yang dapat Anda rumuskan, semakin mungkin semantik yang dihasilkan adalah yang Anda butuhkan dalam penggunaan kehidupan nyata - bagian dari fakta mistik bahwa matematika sangat berguna dalam menangani alam semesta.

Cobalah merumuskan invarian untuk sebuah splitdialek di mana pembatas utama dan pembatasnya dikurung khusus ... kontra-contoh: metode string seperti isspacetidak terlalu sederhana - x.isspace()setara dengan x and all(c in string.whitespace for c in x)- bahwa petunjuk konyol x anditulah mengapa Anda begitu sering menemukan diri Anda sedang membuat kode not x or x.isspace(), untuk kembali ke kesederhanaan yang seharusnya telah dirancang ke dalam is...metode string (di mana string kosong "adalah" apa pun yang Anda inginkan - bertentangan dengan naluri manusia di jalan, mungkin [[set kosong, seperti nol & c, selalu membingungkan kebanyakan orang ;-)]], tetapi sepenuhnya sesuai dengan akal sehat matematis yang sangat halus ! -).

Alex Martelli
sumber
5

Saya tidak yakin jawaban seperti apa yang Anda cari? Anda mendapatkan tiga kecocokan karena Anda memiliki tiga pembatas. Jika Anda tidak ingin yang kosong itu, cukup gunakan:

'/segment/segment/'.strip('/').split('/')
jamieb
sumber
4
-1 karena Anda mendapatkan empat pertandingan bukan tiga, dan ini juga tidak benar-benar menjawab pertanyaan tersebut.
Roman
1
+1 untuk mengatasi neg .. Dia tidak mengatakan Anda akan mendapatkan tiga hasil kembali. Dia mengatakan "tiga pertandingan" untuk "tiga pembatas", yang kedengarannya logis bagi saya. Namun, Anda tidak mendapatkan "empat pertandingan" dari apa pun. Anda mendapatkan "empat elemen" yang dikembalikan dalam hasil Anda. Juga, ini tidak secara langsung menjawab "mengapa", tetapi memberikan cara sederhana untuk mendapatkan apa yang sebenarnya dia inginkan ... yang menurut saya tidak pantas mendapat suara negatif. Jika Anda akan memilih seseorang (dengan suara negatif, tidak kurang) harap lebih berhati-hati! Bersulang! 8 ^)
kodybrown
@wasatchwizard Terima kasih atas klarifikasinya. Saya menghargai koreksi dan rekomendasinya. Sayangnya, sekarang suara saya terkunci dan tidak dapat diubah.
Roman
Saya suka solusi Anda - lepaskan lalu belah untuk menghapus hasil yang kosong
Nam G VU
5

Nah, ini memberi tahu Anda bahwa ada pembatas di sana. Jadi, melihat 4 hasil memungkinkan Anda mengetahui bahwa Anda memiliki 3 pembatas. Ini memberi Anda kekuatan untuk melakukan apa pun yang Anda inginkan dengan informasi ini, daripada meminta Python menjatuhkan elemen kosong, dan kemudian membuat Anda memeriksa secara manual untuk memulai atau mengakhiri pembatas jika Anda perlu mengetahuinya.

Contoh sederhana: Misalnya Anda ingin memeriksa nama file absolut vs. relatif. Dengan cara ini Anda dapat melakukan semuanya dengan split, tanpa harus memeriksa karakter pertama dari nama file Anda.

Roma
sumber
1

Pertimbangkan contoh minimal ini:

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

splitharus memberi Anda apa sebelum dan sesudah pembatas '/', tetapi tidak ada karakter lain. Jadi itu harus memberi Anda string kosong, yang secara teknis mendahului dan mengikuti '/', karena '' + '/' + '' == '/'.

timgeb
sumber