Sebelum Java 8 ketika kita membagi string kosong seperti
String[] tokens = "abc".split("");
mekanisme perpecahan akan terpecah di tempat-tempat yang ditandai dengan |
|a|b|c|
karena ruang kosong ""
ada sebelum dan sesudah setiap karakter. Jadi sebagai hasilnya itu akan menghasilkan array ini pada awalnya
["", "a", "b", "c", ""]
dan nanti akan menghapus sisa string kosong (karena kami tidak secara eksplisit memberikan nilai negatif ke limit
argumen) sehingga akhirnya akan kembali
["", "a", "b", "c"]
Di Java 8 mekanisme split sepertinya telah berubah. Sekarang saat kami menggunakan
"abc".split("")
kita akan mendapatkan ["a", "b", "c"]
larik alih-alih ["", "a", "b", "c"]
jadi sepertinya string kosong di awal juga dihapus. Tetapi teori ini gagal karena misalnya
"abc".split("a")
mengembalikan array dengan string kosong di awal ["", "bc"]
.
Adakah yang bisa menjelaskan apa yang terjadi di sini dan bagaimana aturan pemisahan telah berubah di Java 8?
s.split("(?!^)")
sepertinya berhasil.split("")
daripada samar (untuk orang yang tidak menggunakan regex)split("(?!^)")
atausplit("(?<!^)")
atau beberapa regex lainnya.Jawaban:
Perilaku
String.split
(yang memanggilPattern.split
) berubah antara Java 7 dan Java 8.Dokumentasi
Membandingkan antara dokumentasi
Pattern.split
di Java 7 dan Java 8 , kami mengamati klausa berikut ditambahkan:Klausul yang sama juga ditambahkan
String.split
di Java 8 , dibandingkan dengan Java 7 .Implementasi referensi
Mari kita bandingkan kode
Pattern.split
implemetasi referensi di Java 7 dan Java 8. Kode tersebut diambil dari grepcode, untuk versi 7u40-b43 dan 8-b132.Jawa 7
Jawa 8
Penambahan kode berikut di Java 8 mengecualikan kecocokan panjang-nol di awal string input, yang menjelaskan perilaku di atas.
Menjaga kompatibilitas
Mengikuti perilaku di Java 8 ke atas
Untuk membuat
split
berperilaku secara konsisten di seluruh versi dan kompatibel dengan perilaku di Java 8:(?!\A)
di akhir ekspresi reguler dan bungkus ekspresi reguler asli dalam grup non-penangkap(?:...)
(jika perlu).(?!\A)
memeriksa bahwa string tidak berakhir di awal string, yang berarti bahwa pertandingan tersebut adalah pertandingan kosong di awal string.Mengikuti perilaku di Java 7 dan sebelumnya
Tidak ada solusi umum untuk membuat
split
kompatibel dengan Java 7 dan sebelumnya, selain mengganti semua instancesplit
untuk mengarah ke implementasi kustom Anda sendiri.sumber
split("")
kode sehingga konsisten di berbagai versi java?(?!^)
ke akhir regex dan membungkus regex asli dalam grup non-capturing(?:...)
(jika perlu), tetapi saya tidak dapat memikirkan apa pun cara membuatnya kompatibel ke belakang (ikuti perilaku lama di Java 7 dan sebelumnya)."(?!^)"
? Dalam skenario apa itu akan berbeda""
? (Saya buruk di regex!: - /).Pattern.MULTILINE
bendera, sementara\A
selalu cocok di awal string apa pun benderanya.Ini telah ditentukan dalam dokumentasi
split(String regex, limit)
.Dalam
"abc".split("")
Anda punya pertandingan nol-lebar di awal sehingga substring kosong terkemuka tidak termasuk dalam array yang dihasilkan.Namun dalam cuplikan kedua saat Anda berpisah,
"a"
Anda mendapatkan kecocokan lebar positif (1 dalam kasus ini), jadi substring utama yang kosong disertakan seperti yang diharapkan.(Kode sumber yang tidak relevan dihapus)
sumber
Ada sedikit perubahan dalam dokumen
split()
dari Java 7 ke Java 8. Secara khusus, pernyataan berikut telah ditambahkan:(penekanan saya)
Pemisahan string kosong menghasilkan kecocokan lebar-nol di awal, jadi string kosong tidak disertakan di awal larik yang dihasilkan sesuai dengan yang ditentukan di atas. Sebaliknya, contoh kedua Anda yang berpisah
"a"
menghasilkan kecocokan lebar- positif di awal string, jadi string kosong sebenarnya disertakan di awal larik yang dihasilkan.sumber
"some-string".split("")
adalah kasus yang sangat jarang terjadi..split("")
bukan satu-satunya cara untuk berpisah tanpa mencocokkan apa pun. Kami menggunakan regex lookahead positif yang di jdk7 yang juga cocok di awal dan menghasilkan elemen head kosong yang sekarang hilang. github.com/spray/spray/commit/…