Proyek ICU (yang juga sekarang memiliki perpustakaan PHP ) berisi kelas-kelas yang diperlukan untuk membantu menormalkan string UTF-8 untuk membuatnya lebih mudah untuk membandingkan nilai saat mencari.
Namun, saya mencoba mencari tahu apa artinya ini untuk aplikasi. Misalnya, dalam kasus apa saya ingin "Canonical Equivalence" daripada "Compatibilityivalence", atau sebaliknya?
php
c
unicode
unicode-normalization
Xeoncross
sumber
sumber
(begin curved line) (char1) (char2) … (charN) (end curved line)
daripada ini:(curved line marker prefix) (char1) (curved line marker prefix) (char2) (curved line marker prefix) (char2)
. Dengan kata lain, unit minimal yang bisa dirender?Jawaban:
Semua yang Anda Tidak Pernah Ingin Tahu tentang Normalisasi Unicode
Normalisasi Kanonik
Unicode mencakup banyak cara untuk menyandikan beberapa karakter, terutama karakter beraksen. Normalisasi kanonik mengubah titik kode menjadi bentuk penyandian kanonik. Poin kode yang dihasilkan akan tampak identik dengan yang asli kecuali ada bug di font atau mesin rendering.
Kapan Harus Digunakan
Karena hasilnya tampak identik, selalu aman untuk menerapkan normalisasi kanonik ke string sebelum menyimpan atau menampilkannya, selama Anda dapat mentoleransi hasilnya agar tidak sedikit demi sedikit identik dengan input.
Normalisasi kanonik datang dalam 2 bentuk: NFD dan NFC. Keduanya setara dalam arti bahwa seseorang dapat mengkonversi antara dua bentuk ini tanpa kehilangan. Membandingkan dua string di bawah NFC akan selalu memberikan hasil yang sama dengan membandingkannya di bawah NFD.
NFD
NFD memiliki karakter sepenuhnya diperluas. Ini adalah bentuk normalisasi yang lebih cepat untuk dihitung, tetapi hasilnya lebih banyak poin kode (yaitu menggunakan lebih banyak ruang).
Jika Anda hanya ingin membandingkan dua string yang belum dinormalisasi, ini adalah bentuk normalisasi yang lebih disukai kecuali Anda tahu Anda membutuhkan normalisasi kompatibilitas.
NFC
NFC menggabungkan kembali poin kode bila memungkinkan setelah menjalankan algoritma NFD. Ini membutuhkan waktu sedikit lebih lama, tetapi menghasilkan string yang lebih pendek.
Normalisasi Kompatibilitas
Unicode juga mencakup banyak karakter yang benar-benar bukan milik, tetapi digunakan dalam rangkaian karakter sebelumnya. Unicode menambahkan ini untuk memungkinkan teks dalam set karakter tersebut diproses sebagai Unicode, dan kemudian dikonversi kembali tanpa kehilangan.
Normalisasi kompatibilitas mengonversi ini ke urutan yang sesuai dari karakter "nyata", dan juga melakukan normalisasi kanonik. Hasil normalisasi kompatibilitas mungkin tidak tampak identik dengan aslinya.
Karakter yang menyertakan informasi pemformatan diganti dengan karakter yang tidak. Misalnya karakter
⁹
dikonversi ke9
. Lainnya tidak melibatkan perbedaan format. Misalnya karakter angka romawiⅨ
dikonversi ke huruf biasaIX
.Tentunya, setelah transformasi ini dilakukan, tidak mungkin lagi untuk mengubah kembali lossless ke set karakter asli.
Kapan harus digunakan
Konsorsium Unicode menyarankan pemikiran normalisasi kompatibilitas seperti
ToUpperCase
transformasi. Ini adalah sesuatu yang mungkin berguna dalam beberapa keadaan, tetapi Anda tidak boleh hanya menerapkannya saja.Kasing yang bagus akan menjadi mesin pencari karena Anda mungkin ingin mencari yang
9
cocok⁹
.Satu hal yang mungkin tidak boleh Anda lakukan adalah menampilkan hasil penerapan normalisasi kompatibilitas kepada pengguna.
NFKC / NFKD
Bentuk normalisasi kompatibilitas datang dalam dua bentuk NFKD dan NFKC. Mereka memiliki hubungan yang sama seperti antara NFD dan C.
Setiap string dalam NFKC secara inheren juga dalam NFC, dan sama untuk NFKD dan NFD. Jadi
NFKD(x)=NFD(NFKC(x))
, danNFKC(x)=NFC(NFKD(x))
, dll.Kesimpulan
Jika ragu, lakukan normalisasi kanonik. Pilih NFC atau NFD berdasarkan pertukaran ruang / kecepatan yang berlaku, atau berdasarkan apa yang dibutuhkan oleh sesuatu yang Anda inter-operasi.
sumber
NFC(x)=Recompose(NFD(x))
.Beberapa karakter, misalnya huruf dengan aksen (katakanlah
é
) dapat direpresentasikan dalam dua cara - satu titik kodeU+00E9
atau huruf biasa diikuti dengan tanda aksen yang menggabungkanU+0065 U+0301
. Normalisasi biasa akan memilih salah satunya untuk selalu mewakilinya (titik kode tunggal untuk NFC, bentuk penggabungan untuk NFD).Untuk karakter yang dapat diwakili oleh beberapa urutan karakter dasar dan menggabungkan tanda (katakanlah, "s, titik di bawah, titik di atas" vs menempatkan titik di atas kemudian di bawah titik atau menggunakan karakter dasar yang sudah memiliki salah satu dari titik-titik), NFD akan juga memilih salah satu dari ini (di bawah ini berjalan lebih dulu, seperti yang terjadi)
Dekomposisi kompatibilitas menyertakan sejumlah karakter yang "seharusnya tidak benar-benar" menjadi karakter tetapi karena mereka digunakan dalam pengkodean sebelumnya. Normalisasi biasa tidak akan menyatukan ini (untuk menjaga integritas bolak-balik - ini bukan masalah untuk menggabungkan formulir karena tidak ada penyandian sebelumnya [kecuali beberapa penyandian Vietnam] menggunakan keduanya), tetapi normalisasi kompatibilitas akan. Pikirkan seperti tanda "kg" kilogram yang muncul dalam beberapa penyandian Asia Timur (atau katakana dan alfabet setengah lebar / lebar pita lebar), atau ligatur "fi" di MacRoman.
Lihat http://unicode.org/reports/tr15/ untuk lebih jelasnya.
sumber
Bentuk normal (dari Unicode, bukan basis data) berurusan terutama (secara eksklusif?) Dengan karakter yang memiliki tanda diakritik. Unicode memberi beberapa karakter tanda "built in" diakritik, seperti U + 00C0, "Latin Capital A with Grave". Karakter yang sama dapat dibuat dari `Latin Capital A" (U + 0041) dengan "Combining Grave Accent" (U + 0300). Itu berarti meskipun dua sekuens menghasilkan karakter hasil yang sama, byte-by-byte perbandingan akan menunjukkan bahwa mereka sama sekali berbeda.
Normalisasi adalah upaya menghadapi itu. Normalisasi menjamin (atau setidaknya mencoba) bahwa semua karakter dikodekan dengan cara yang sama - baik semua menggunakan tanda gabungan diakritik terpisah jika diperlukan, atau semua menggunakan titik kode tunggal sedapat mungkin. Dari sudut pandang perbandingan, tidak banyak masalah yang Anda pilih - cukup banyak string yang dinormalisasi akan membandingkan dengan benar dengan string yang dinormalisasi lainnya.
Dalam hal ini, "kompatibilitas" berarti kompatibilitas dengan kode yang mengasumsikan bahwa satu titik kode sama dengan satu karakter. Jika Anda memiliki kode seperti itu, Anda mungkin ingin menggunakan formulir normal kompatibilitas. Meskipun saya belum pernah melihatnya secara langsung, nama-nama bentuk normal menyiratkan bahwa konsorsium Unicode menganggapnya lebih baik menggunakan tanda gabungan diakritik terpisah. Ini membutuhkan lebih banyak kecerdasan untuk menghitung karakter aktual dalam string (serta hal-hal seperti memecah string secara cerdas), tetapi lebih fleksibel.
Jika Anda menggunakan ICU sepenuhnya, kemungkinan Anda ingin menggunakan bentuk normal kanonik. Jika Anda mencoba menulis kode sendiri yang (misalnya) mengasumsikan titik kode sama dengan karakter, maka Anda mungkin menginginkan bentuk normal kompatibilitas yang menjadikannya sesering mungkin.
sumber
"o\x{332}\x{303}\x{304}"
, dan NFC adalah"\x{22D}\x{332}"
. Untuk NFD kedua adalah"o\x{332}\x{304}\x{303}"
dan NFC"\x{14D}\x{332}\x{303}"
. Namun, ada banyak kemungkinan non-kanonik yang secara kanonik setara dengan ini. Normalisasi memungkinkan perbandingan biner dari grafem setara kanonik.Jika dua string unicode setara dengan kanonik string benar-benar sama, hanya menggunakan urutan unicode yang berbeda. Misalnya Ä dapat direpresentasikan dengan menggunakan karakter Ä atau kombinasi A dan ◌̈.
Jika string hanya setara dengan kompatibilitas, string tidak harus sama, tetapi mereka mungkin sama dalam beberapa konteks. Misalnya ff dapat dianggap sama dengan ff.
Jadi, jika Anda membandingkan string, Anda harus menggunakan kesetaraan kanonik, karena kesetaraan kompatibilitas bukan kesetaraan nyata.
Tetapi jika Anda ingin mengurutkan serangkaian string, mungkin masuk akal untuk menggunakan kesetaraan kompatibilitas karena hampir identik.
sumber
Ini sebenarnya cukup sederhana. UTF-8 sebenarnya memiliki beberapa representasi berbeda dari "karakter" yang sama. (Saya menggunakan karakter dalam tanda kutip karena byte-bijaksana mereka berbeda, tetapi praktis mereka sama). Contoh diberikan dalam dokumen tertaut.
Karakter "Ç" dapat direpresentasikan sebagai urutan byte 0xc387. Tetapi juga dapat diwakili oleh
C
(0x43) diikuti oleh urutan byte 0xcca7. Jadi Anda dapat mengatakan bahwa 0xc387 dan 0x43cca7 adalah karakter yang sama. Alasan yang berhasil, adalah 0xcca7 adalah tanda gabungan; artinya mengatakan dibutuhkan karakter sebelum (diC
sini), dan memodifikasinya.Sekarang, sejauh perbedaan antara kesetaraan kanonik vs kesetaraan kompatibilitas, kita perlu melihat karakter secara umum.
Ada 2 jenis karakter, yang menyampaikan makna melalui nilai , dan yang mengambil karakter lain dan mengubahnya. 9 adalah karakter yang bermakna. Skrip super ⁹ mengambil makna itu dan mengubahnya dengan presentasi. Jadi secara kanonik mereka memiliki makna yang berbeda, tetapi mereka masih mewakili karakter dasar.
Kesetaraan kanonik adalah di mana urutan byte menghasilkan karakter yang sama dengan makna yang sama. Kesetaraan kompatibilitas adalah ketika urutan byte menghasilkan karakter yang berbeda dengan arti dasar yang sama (meskipun mungkin diubah). 9 dan ⁹ adalah setara kompatibilitas karena keduanya berarti "9", tetapi tidak setara secara kanonik karena mereka tidak memiliki representasi yang sama.
sumber
Apakah kesetaraan kanonik atau kesetaraan kompatibilitas lebih relevan bagi Anda tergantung pada aplikasi Anda. Cara berpikir ASCII tentang perbandingan string secara kasar memetakan ke ekuivalensi kanonik, tetapi Unicode mewakili banyak bahasa. Saya rasa tidak aman untuk menganggap bahwa Unicode mengkodekan semua bahasa dengan cara yang memungkinkan Anda memperlakukan mereka seperti ASCII Eropa Barat.
Gambar 1 dan 2 memberikan contoh yang baik dari dua jenis kesetaraan. Di bawah kesetaraan kompatibilitas, sepertinya angka yang sama dalam bentuk sub-dan super-script akan membandingkan sama. Tapi saya tidak yakin itu menyelesaikan masalah yang sama seperti bentuk arab kursif atau karakter yang diputar.
Kebenaran sulit dari pemrosesan teks Unicode adalah bahwa Anda harus berpikir secara mendalam tentang persyaratan pemrosesan teks aplikasi Anda, dan kemudian mengatasinya sebaik mungkin dengan alat yang tersedia. Itu tidak secara langsung menjawab pertanyaan Anda, tetapi jawaban yang lebih terperinci akan membutuhkan ahli linguistik untuk setiap bahasa yang Anda harapkan untuk didukung.
sumber
Masalah membandingkan string : dua string dengan konten yang setara untuk keperluan sebagian besar aplikasi mungkin mengandung urutan karakter yang berbeda.
Lihat kesetaraan kanonik Unicode : jika algoritma perbandingan sederhana (atau harus cepat), kesetaraan Unicode tidak dilakukan. Masalah ini terjadi, misalnya, dalam perbandingan kanonik XML, lihat http://www.w3.org/TR/xml-c14n
Untuk menghindari masalah ini ... Standar apa yang digunakan? "extended UTF8" atau "compact UTF8"?
Gunakan "ç" atau "c + ◌̧."?
W3C dan lainnya (mis. Nama file ) menyarankan untuk menggunakan "disusun sebagai kanonik" (perhatikan C dari string "paling ringkas" lebih pendek) ... Jadi,
Standarnya adalah C ! ragu menggunakan NFC
Untuk interoperabilitas, dan untuk pilihan "konvensi konfigurasi" , rekomendasinya adalah penggunaan NFC , untuk "mengkanonisasi" string eksternal. Untuk menyimpan XML kanonik, misalnya, simpan di "FORM_C". CSV W3C pada Kelompok Kerja Web juga merekomendasikan NFC (bagian 7.2).
PS: de "FORM_C" adalah bentuk default di sebagian besar perpustakaan. Ex. dalam normalizer.isnormalized PHP () .
Istilah " bentuk kompos " (
FORM_C
) digunakan untuk keduanya, untuk mengatakan bahwa "string dalam bentuk C-kanonik" (hasil dari transformasi NFC) dan untuk mengatakan bahwa algoritma transformasi digunakan ... Lihat http: //www.macchiato.com/unicode/nfc-faqCatatan: untuk menguji normalisasi string kecil (UTF-8 murni atau referensi entitas XML), Anda dapat menggunakan tes ini / menormalkan konverter online .
sumber