Karakter π©βπ©βπ§βπ¦ (keluarga dengan dua wanita, satu perempuan, dan satu laki-laki) dikodekan sebagai berikut:
U+1F469
WOMAN
,
βU+200D
ZWJ
,
U+1F469
WOMAN
,
U+200D
ZWJ
,
U+1F467
GIRL
,
U+200D
ZWJ
,
U+1F466
BOY
Jadi itu sangat menarik dikodekan; target sempurna untuk unit test. Namun, Swift sepertinya tidak tahu bagaimana mengobatinya. Inilah yang saya maksud:
"π©βπ©βπ§βπ¦".contains("π©βπ©βπ§βπ¦") // true
"π©βπ©βπ§βπ¦".contains("π©") // false
"π©βπ©βπ§βπ¦".contains("\u{200D}") // false
"π©βπ©βπ§βπ¦".contains("π§") // false
"π©βπ©βπ§βπ¦".contains("π¦") // true
Jadi, Swift mengatakan itu berisi dirinya sendiri (baik) dan anak laki-laki (baik!). Tetapi dikatakan bahwa itu tidak mengandung seorang wanita, gadis, atau joiner dengan lebar nol. Apa yang sedang terjadi disini? Mengapa Swift tahu itu mengandung anak laki-laki tetapi bukan perempuan atau perempuan? Saya bisa mengerti jika itu diperlakukan sebagai karakter tunggal dan hanya mengenalinya mengandung dirinya sendiri, tetapi fakta bahwa ia mendapat satu subkomponen dan tidak ada yang lain membuatku bingung.
Ini tidak berubah jika saya menggunakan sesuatu seperti "π©".characters.first!
.
Yang lebih membingungkan adalah ini:
let manual = "\u{1F469}\u{200D}\u{1F469}\u{200D}\u{1F467}\u{200D}\u{1F466}"
Array(manual.characters) // ["π©β", "π©β", "π§β", "π¦"]
Meskipun saya menempatkan ZWJ di sana, mereka tidak tercermin dalam array karakter. Yang terjadi selanjutnya sedikit memberi tahu:
manual.contains("π©") // false
manual.contains("π§") // false
manual.contains("π¦") // true
Jadi saya mendapatkan perilaku yang sama dengan array karakter ... yang sangat menjengkelkan, karena saya tahu seperti apa array itu.
Ini juga tidak berubah jika saya menggunakan sesuatu seperti "π©".characters.first!
.
"π©βπ©βπ§βπ¦".contains("\u{200D}")
masih mengembalikan false, tidak yakin apakah itu bug atau fitur.Jawaban:
Ini ada hubungannya dengan bagaimana
String
tipe ini bekerja di Swift, dan bagaimanacontains(_:)
metode ini bekerja.'π©βπ©βπ§βπ¦' adalah apa yang dikenal sebagai urutan emoji, yang diterjemahkan sebagai satu karakter yang terlihat dalam sebuah string. Urutan terdiri dari
Character
objek, dan pada saat yang sama terdiri dariUnicodeScalar
objek.Jika Anda memeriksa jumlah karakter string, Anda akan melihat bahwa itu terdiri dari empat karakter, sedangkan jika Anda memeriksa jumlah skalar unicode, itu akan menunjukkan kepada Anda hasil yang berbeda:
Sekarang, jika Anda mem-parsing melalui karakter dan mencetaknya, Anda akan melihat apa yang tampak seperti karakter normal, tetapi sebenarnya tiga karakter pertama berisi emoji serta joiner lebar nol di dalamnya
UnicodeScalarView
:Seperti yang Anda lihat, hanya karakter terakhir yang tidak mengandung joiner lebar nol, jadi ketika menggunakan
contains(_:)
metode ini, ia berfungsi seperti yang Anda harapkan. Karena Anda tidak membandingkan emoji yang mengandung penggabung lebar nol, metode ini tidak akan menemukan kecocokan untuk apa pun kecuali karakter terakhir.Untuk memperluas ini, jika Anda membuat
String
yang terdiri dari karakter emoji yang diakhiri dengan penggabung lebar nol, dan meneruskannya kecontains(_:)
metode, itu juga akan dievaluasifalse
. Ini berkaitan dengancontains(_:)
persis sama denganrange(of:) != nil
, yang mencoba menemukan kecocokan yang tepat dengan argumen yang diberikan. Karena karakter yang diakhiri dengan joiner lebar nol membentuk urutan yang tidak lengkap, metode ini mencoba untuk menemukan kecocokan untuk argumen sambil menggabungkan karakter yang diakhiri dengan joiner lebar nol ke dalam urutan yang lengkap. Ini berarti bahwa metode ini tidak akan pernah menemukan kecocokan jika:Untuk menunjukkan:
Namun, karena perbandingan hanya melihat ke depan, Anda dapat menemukan beberapa urutan lengkap lainnya dalam string dengan bekerja mundur:
Solusi termudah adalah dengan memberikan opsi perbandingan spesifik untuk
range(of:options:range:locale:)
metode ini. OpsiString.CompareOptions.literal
melakukan perbandingan pada kesetaraan karakter per karakter yang tepat . Sebagai catatan, apa yang dimaksud dengan karakter di sini bukanlah SwiftCharacter
, tetapi representasi UTF-16 dari instance dan string perbandingan - namun, karenaString
tidak mengizinkan UTF-16 cacat, ini pada dasarnya setara dengan membandingkan skalar Unicode perwakilan.Di sini saya telah membebani
Foundation
metode ini, jadi jika Anda membutuhkan yang asli, ganti nama ini atau apalah:Sekarang metode ini berfungsi sebagaimana mestinya "harus" dengan setiap karakter, bahkan dengan urutan yang tidak lengkap:
sumber
"π©βπ©βπ§βπ¦".count
mengevaluasi1
dengan Xcode 9 beta saat ini dan Swift 4.Masalah pertama adalah Anda menjembatani ke Yayasan dengan
contains
(SwiftString
bukan aCollection
), jadi ini adalahNSString
perilaku, yang saya tidak percaya menangani Emoji yang dikomposisi sekuat Swift. Yang mengatakan, Swift saya percaya sedang mengimplementasikan Unicode 8 sekarang, yang juga perlu revisi di sekitar situasi ini di Unicode 10 (jadi ini semua dapat berubah ketika mereka menerapkan Unicode 10; Saya belum menggali apakah mau atau tidak).Untuk mempermudah, mari singkirkan Foundation, dan gunakan Swift, yang memberikan pandangan yang lebih eksplisit. Kami akan mulai dengan karakter:
BAIK. Itu yang kami harapkan. Tapi itu bohong. Mari kita lihat apa sebenarnya karakter-karakter itu.
Ah ... Jadi begitu
["π©ZWJ", "π©ZWJ", "π§ZWJ", "π¦"]
. Itu membuat semuanya sedikit lebih jelas. π© bukan anggota dari daftar ini (ini "π©ZWJ"), tetapi π¦ adalah anggota.Masalahnya adalah itu
Character
adalah "cluster grapheme," yang menyusun berbagai hal bersama-sama (seperti melampirkan ZWJ). Apa yang sebenarnya Anda cari adalah skalar unicode. Dan itu bekerja persis seperti yang Anda harapkan:Dan tentu saja kita juga dapat mencari karakter aktual yang ada di sana:
(Ini duplikat poin Ben Leggiero. Saya memposting ini sebelum memperhatikan dia menjawab. Meninggalkan kalau-kalau itu lebih jelas bagi siapa pun.)
sumber
ZWJ
berdiri?String
diduga diubah kembali menjadi tipe koleksi. Apakah itu mempengaruhi jawaban Anda?Tampaknya Swift menganggap a
ZWJ
menjadi cluster grapheme yang diperluas dengan karakter yang mendahuluinya. Kita bisa melihat ini ketika memetakan array karakter keunicodeScalars
:Ini mencetak yang berikut dari LLDB:
Selain itu,
.contains
grup memperluas cluster grapheme menjadi satu karakter. Misalnya, mengambil karakter hangulα
,α ‘
danα«
(yang menggabungkan untuk membuat kata Korea untuk "satu":αα ‘α«
):Ini tidak dapat menemukan
α
karena tiga codepoint dikelompokkan ke dalam satu cluster yang bertindak sebagai satu karakter. Demikian pula,\u{1F469}\u{200D}
(WOMAN
ZWJ
) adalah satu cluster, yang bertindak sebagai satu karakter.sumber
Jawaban lain membahas apa yang dilakukan Swift, tetapi jangan terlalu mendetail tentang mengapa.
Apakah Anda berharap "Γ " sama dengan "Γ "? Saya harap Anda akan melakukannya.
Salah satunya adalah surat dengan combiner, yang lain adalah karakter yang terdiri tunggal. Anda dapat menambahkan banyak penggabung berbeda ke karakter dasar, dan manusia masih akan menganggapnya sebagai karakter tunggal. Untuk menangani perbedaan ini, konsep grapheme dibuat untuk mewakili apa yang manusia anggap sebagai karakter terlepas dari codepoint yang digunakan.
Sekarang layanan pesan teks telah menggabungkan karakter ke emoji grafis selama bertahun-tahun
:)
Β βΒπ
. Jadi berbagai emoji ditambahkan ke Unicode.Layanan ini juga mulai menggabungkan emoji bersama menjadi emoji komposit.
Tentu saja tidak ada cara yang masuk akal untuk menyandikan semua kombinasi yang mungkin menjadi titik-titik kod tersendiri, jadi The Unicode Consortium memutuskan untuk memperluas konsep grapheme untuk mencakup karakter-karakter komposit ini.
Apa ini intinya adalah
"π©βπ©βπ§βπ¦"
harus dianggap sebagai "grapheme cluster" tunggal jika Anda mencoba untuk bekerja dengannya di tingkat grapheme, seperti yang dilakukan Swift secara default.Jika Anda ingin memeriksa apakah mengandung
"π¦"
sebagai bagian dari itu, maka Anda harus turun ke level yang lebih rendah.Saya tidak tahu sintaks Swift jadi di sini ada beberapa Perl 6 yang memiliki tingkat dukungan yang sama untuk Unicode.
(Perl 6 mendukung Unicode versi 9 sehingga mungkin ada perbedaan)
Mari kita turun satu level
Naik ke level ini bisa membuat beberapa hal lebih sulit.
Saya berasumsi bahwa
.contains
dalam Swift membuatnya lebih mudah, tetapi itu tidak berarti tidak ada hal lain yang menjadi lebih sulit.Bekerja pada level ini membuatnya lebih mudah untuk secara tidak sengaja memecah string di tengah karakter komposit misalnya.
Apa yang secara tidak sengaja Anda tanyakan adalah mengapa representasi level yang lebih tinggi ini tidak berfungsi seperti representasi level yang lebih rendah. Jawabannya tentu saja, itu tidak seharusnya.
Jika Anda bertanya pada diri sendiri " mengapa ini harus begitu rumit ", jawabannya tentu saja " manusia ".
sumber
rotor
dangrep
lakukan di sini? Dan apa itu1-$l
?rotor
,. Kodesay (1,2,3,4,5,6).rotor(3)
menghasilkan((1 2 3) (4 5 6))
. Itu daftar daftar, masing-masing panjangnya3
.say (1,2,3,4,5,6).rotor(3=>-2)
menghasilkan yang sama kecuali sublist kedua dimulai dengan2
bukannya4
, yang ketiga dengan3
, dan seterusnya, menghasilkan((1 2 3) (2 3 4) (3 4 5) (4 5 6))
. Jika@match
mengandung"π©βπ©βπ§βπ¦".ords
maka kode @ Brad hanya membuat satu sublist, jadi=>1-$l
bitnya tidak relevan (tidak digunakan). Hanya relevan jika@match
lebih pendek dari@components
.grep
mencoba untuk mencocokkan setiap elemen dalam undangannya (dalam hal ini, daftar sublists dari@components
). Ia mencoba untuk mencocokkan setiap elemen terhadap argumen pencocokannya (dalam hal ini,@match
). The.Bool
kemudian kembaliTrue
jika dan hanya jikagrep
menghasilkan setidaknya satu pertandingan.Pembaruan Swift 4.0
String menerima banyak revisi dalam pembaruan Swift 4, seperti yang didokumentasikan dalam SE-0163 . Dua emoji digunakan untuk demo ini yang mewakili dua struktur berbeda. Keduanya dikombinasikan dengan urutan emoji.
ππ½
adalah kombinasi dari dua emoji,π
danπ½
π©βπ©βπ§βπ¦
adalah kombinasi empat emoji, dengan penghubung lebar nol tersambung. Formatnya adalahπ©βjoinerπ©βjoinerπ§βjoinerπ¦
1. Hitungan
Dalam Swift 4.0 emoji dihitung sebagai cluster grapheme. Setiap emoji dihitung sebagai 1.
count
Properti ini juga tersedia secara langsung untuk string. Jadi Anda bisa langsung menyebutnya seperti ini.Larik karakter string juga dihitung sebagai cluster grapheme di Swift 4.0, sehingga kedua kode berikut mencetak 1. Kedua emoji ini adalah contoh dari urutan emoji, di mana beberapa emoji digabungkan bersama-sama dengan atau tanpa penggabung lebar nol di
\u{200d}
antara mereka. Pada swift 3.0, array karakter string tersebut memisahkan setiap emoji dan menghasilkan array dengan banyak elemen (emoji). Joiner diabaikan dalam proses ini. Namun, dalam Swift 4.0, array karakter melihat semua emoji sebagai satu kesatuan. Sehingga emoji apa pun akan selalu menjadi 1.unicodeScalars
tetap tidak berubah dalam Swift 4. Ini memberikan karakter Unicode unik dalam string yang diberikan.2. Berisi
Di Swift 4.0,
contains
metode mengabaikan join lebar nol di emoji. Jadi mengembalikan true untuk salah satu dari empat komponen emoji"π©βπ©βπ§βπ¦"
, dan mengembalikan false jika Anda memeriksa penggabung. Namun, di Swift 3.0, joiner tidak diabaikan dan digabungkan dengan emoji di depannya. Jadi, ketika Anda memeriksa apakah"π©βπ©βπ§βπ¦"
berisi tiga emoji komponen pertama, hasilnya akan salahsumber
Emoji, seperti standar unicode, sangat rumit. Nada kulit, jenis kelamin, pekerjaan, kelompok orang, urutan pengganda lebar nol, bendera (2 karakter unicode) dan komplikasi lainnya dapat membuat parsing emoji berantakan. Pohon Natal, Sepotong Pizza, atau Tumpukan Poop dapat diwakili dengan satu titik kode Unicode. Belum lagi ketika emoji baru diperkenalkan, ada penundaan antara dukungan iOS dan rilis emoji. Itu dan fakta bahwa berbagai versi iOS mendukung versi standar unicode yang berbeda.
TL; DR. Saya telah bekerja pada fitur-fitur ini dan membuka sumber perpustakaan saya adalah penulis untuk JKEmoji untuk membantu string parse dengan emoji. Itu membuat penguraian semudah:
Itu dilakukan dengan memperbarui basis data lokal dari semua emoji yang dikenali sebagai versi unicode terbaru ( 12.0 baru-baru ini) dan merujuk silang mereka dengan apa yang dikenali sebagai emoji yang valid dalam versi OS yang sedang berjalan dengan melihat representasi bitmap dari karakter emoji yang tidak dikenal.
CATATAN
Jawaban sebelumnya dihapus untuk mengiklankan perpustakaan saya tanpa menyatakan dengan jelas bahwa saya adalah pembuatnya. Saya mengakui ini lagi.
sumber