Mengapa "memisahkan" pada string kosong mengembalikan larik yang tidak kosong?

111

Split pada string kosong mengembalikan larik berukuran 1:

scala> "".split(',')
res1: Array[String] = Array("")

Pertimbangkan bahwa ini mengembalikan array kosong:

scala> ",,,,".split(',')
res2: Array[String] = Array()

Tolong jelaskan :)

oluies
sumber
5
Selain itu, tampaknya tidak konsisten dengan perilaku yang diamati saat string hanya berisi satu contoh pemisah. Dalam hal ini hasilnya adalah array kosong: ",". Split (","). Length == 0
LD.

Jawaban:

37

Untuk alasan yang sama itu

",test" split ','

dan

",test," split ','

akan mengembalikan larik berukuran 2. Segala sesuatu sebelum pertandingan pertama dikembalikan sebagai elemen pertama.

Daniel C. Sobral
sumber
5
String kosong adalah string, bukan tidak ada. (di mana saja kecuali di Excel)
Raphael
5
@Raphael Atau dalam database Oracle
Austin
7
@Raphael, dalam bahasa pemrograman lain "".split("wtf").lengthmenghasilkan 0. Hanya di JS 1.: /
Andrey Mikhaylov - lolmaus
11
@ DanielC.Sobral Oke, jadi mengapa "," split ","mengembalikan array 0?
Joan
5
Mengapa tidak semuanya setelah pertandingan terakhir kembali juga?
Didier A.
72

Jika Anda membagi jeruk nol kali, Anda mendapatkan tepat satu bagian - jeruk.

Sam Stainsby
sumber
8
Tapi jeruknya tidak kosong (idk kalau itu yang dimaksud oluies), itu jeruk. Mungkin membelah jeruk yang seharusnya ada, tetapi sebenarnya tidak, jadi Anda mendapatkan kembali satu nilai: ruang kosong xD
Nick Rolando
8
Ini adalah percakapan yang mendalam.
31
Metafora ini masuk akal "orange".split(','), tetapi tidak jelas relevan untuk memisahkan string kosong. Jika saya membagi kekurangan jeruk saya nol kali, saya masih tidak memiliki jeruk; apakah kita menyatakan itu sebagai daftar kosong tanpa jeruk, daftar persis satu tidak ada jeruk, daftar dua belas tidak ada jeruk, atau apa? Ini bukan pertanyaan tentang apa yang akan kita dapatkan, tapi bagaimana kita mewakilinya.
Matchu
1
Tetapi jika Anda memisahkan buku yang tidak ada dengan halaman-halamannya, Anda tidak akan mendapatkan apa-apa.
SMUsamaShah
49

Metode pemisahan Java dan Scala beroperasi dalam dua langkah seperti ini:

  • Pertama, pisahkan string dengan pembatas. Konsekuensi alami adalah jika string tidak berisi pemisah, array tunggal yang hanya berisi string input dikembalikan,
  • Kedua, hapus semua string kosong paling kanan. Inilah alasannya ",,,".split(",")mengembalikan array kosong.

Menurut ini, hasil dari "".split(",")harus berupa larik kosong karena langkah kedua, bukan?

Itu harus. Sayangnya, ini adalah casing sudut yang diperkenalkan secara artifisial. Dan yang buruk, tapi setidaknya itu didokumentasikan dalam java.util.regex.Pattern, jika Anda ingat untuk melihat dokumentasi:

Untuk n == 0, hasilnya adalah untuk n <0, kecuali string kosong tidak akan dikembalikan. (Perhatikan bahwa kasus di mana masukan itu sendiri adalah string kosong yang khusus, seperti dijelaskan di atas, dan parameter batas tidak berlaku di sana.)

Solusi 1: Selalu berikan -1 sebagai parameter kedua

Jadi, saya menyarankan Anda untuk selalu lulus n == -1sebagai parameter kedua (ini akan melewatkan langkah kedua di atas), kecuali Anda secara khusus tahu apa yang ingin Anda capai / Anda yakin bahwa string kosong bukanlah sesuatu yang akan didapat program Anda sebagai input.

Solusi 2: Gunakan kelas Guava Splitter

Jika Anda sudah menggunakan Guava dalam proyek Anda, Anda dapat mencoba kelas Splitter (dokumentasi) . Ini memiliki API yang sangat kaya, dan membuat kode Anda sangat mudah dipahami.

Splitter.on(".").split(".a.b.c.") // "", "a", "b", "c", ""
Splitter.on(",").omitEmptyStrings().split("a,,b,,c") // "a", "b", "c"
Splitter.on(CharMatcher.anyOf(",.")).split("a,b.c") // "a", "b", "c"
Splitter.onPattern("=>?").split("a=b=>c") // "a", "b", "c"
Splitter.on(",").limit(2).split("a,b,c") // "a", "b,c"
Rok Kralj
sumber
1
+1, ini adalah satu-satunya jawaban yang benar-benar mengutip dokumentasi dan menunjukkan bahwa itu tidak konsisten. Namun, saya tidak menemukan bagian yang disorot dari komentar tersebut di JavaDoc saya.
Yogu
Saya telah menemukannya di java.util.regex.Pattern, tetapi tampaknya sebagian besar telah hilang. Pada saat penulisan, ini pasti ada di pohon sumber resmi OpenJDK sebagai javadoc. android.googlesource.com/platform/libcore/+/… Mungkin kita harus melaporkan bug?
Rok Kralj
Akan menjadi ide yang baik untuk melaporkan bug - perilakunya pasti tidak akan diubah, tetapi setidaknya harus didokumentasikan.
Yogu
@RokKralj Android tidak menggunakan pustaka OpenJDK, melainkan berdasarkan Apache Harmony, jadi mungkin Anda mencari di tempat yang salah?
lxgr
1
"".split (",", n)menghasilkan satu elemen array untuk n di (-1, 0, 1) dengan Oracle JDK 8. Akan menyenangkan untuk mendapatkan daftar token yang tidak kosong saja - tebak regex lengkap mungkin diperlukan (seperti "[^,\\s]+[^,]*[^,\\s]*").
simon.watts
40

Memisahkan string kosong mengembalikan string kosong sebagai elemen pertama. Jika tidak ada pembatas yang ditemukan dalam string target, Anda akan mendapatkan array berukuran 1 yang menahan string asli, meskipun kosong.

Nick Rolando
sumber
2
Salah. Split menghapus semua string kosong paling kanan, oleh karena itu hasilnya harus berupa array kosong. Lihat jawaban saya. ",".split(",")mengembalikan array kosong.
Rok Kralj
23

"a".split(",")-> "a" karena itu "".split(",")->""

weberjn
sumber
6
Salah. Split menghapus semua string kosong paling kanan, oleh karena itu hasilnya harus berupa array kosong. Lihat jawaban saya. ",".split(",")mengembalikan array kosong.
Rok Kralj
5

Dalam semua bahasa pemrograman, saya tahu string kosong masih merupakan String yang valid. Jadi melakukan pemisahan menggunakan pemisah apa pun akan selalu mengembalikan larik elemen tunggal di mana elemen itu adalah String kosong. Jika itu adalah String null (tidak kosong) maka itu akan menjadi masalah yang berbeda.

brent777
sumber
Saya pikir ini adalah fungsi perpustakaan dan bukan bagian dari bahasa. Misalnya di google guava Anda bisa menghilangkan string kosong. > Iterable <String> pieces = com.google.common.base.Splitter.on (','). OmitEmptyStrings (). Split ("");
oluies
2

Ini splitperilaku diwariskan dari Jawa, untuk lebih baik atau lebih buruk ...
Scala tidak mengesampingkan definisi dari Stringprimitif.

Catatan, Anda bisa menggunakan limitargumen untuk mengubah perilaku :

Parameter batas mengontrol berapa kali pola diterapkan dan karenanya memengaruhi panjang larik yang dihasilkan. Jika batas n lebih besar dari nol maka pola akan diterapkan paling banyak n - 1 kali, panjang larik tidak akan lebih dari n, dan entri terakhir larik akan berisi semua masukan di luar pembatas yang cocok terakhir. Jika n non-positif maka pola akan diterapkan sebanyak mungkin dan array dapat memiliki panjang berapa pun. Jika n adalah nol maka pola akan diterapkan sebanyak mungkin, array dapat memiliki panjang berapa pun, dan string kosong yang tertinggal akan dibuang.

yaitu Anda dapat menyetel limit=-1untuk mendapatkan perilaku (semua?) bahasa lain:

@ ",a,,b,,".split(",")
res1: Array[String] = Array("", "a", "", "b")

@ ",a,,b,,".split(",", -1)  // limit=-1
res2: Array[String] = Array("", "a", "", "b", "", "")

Tampaknya terkenal bahwa perilaku Java cukup membingungkan tetapi:

Perilaku di atas dapat diamati setidaknya dari Java 5 hingga Java 8.

Ada upaya untuk mengubah perilaku untuk mengembalikan larik kosong saat memisahkan string kosong di JDK-6559590 . Namun, itu segera dikembalikan ke JDK-8028321 ketika menyebabkan regresi di berbagai tempat. Perubahan tidak pernah membuatnya menjadi rilis awal Java 8.

Catatan: Metode pemisahan tidak ada di Java sejak awal ( bukan di 1.0.2 ) tetapi sebenarnya ada setidaknya dari 1.4 (mis. Lihat JSR51 sekitar tahun 2002). Saya masih menyelidiki ...

Apa yang tidak jelas adalah mengapa Java memilih ini di tempat pertama (kecurigaan saya adalah bahwa ini pada awalnya merupakan kekeliruan / bug dalam "kasus tepi"), tetapi sekarang dimasukkan ke dalam bahasa yang tidak dapat ditarik kembali dan tetap demikian .

Andy Hayden
sumber
Saya tidak yakin ini menjawab pertanyaan - meskipun mungkin benar untuk contoh yang diberikan di sini, itu tidak membantu dengan kasus string kosong - "".split(",")masih mengembalikan array elemen tunggal seperti [""].
DaveyDaveDave
@DaveyDaveDave itulah perilaku yang diharapkan dari setiap bahasa lain. ",,,," adalah perilaku aneh / berbeda di Scala, dan berbeda dengan kasus "".
Andy Hayden
0

String kosong tidak memiliki status khusus saat memisahkan string. Anda dapat menggunakan:

Some(str)
  .filter(_ != "")
  .map(_.split(","))
  .getOrElse(Array())
Hanan Oanunu
sumber