Normalisasi Unicode

Unicode memiliki empat bentuk normalisasi dan keempatnya tidak dapat saling dipertukarkan. NFC dan NFD menata ulang karakter yang sama antara satu titik kode dan basis ditambah tanda kombinasi; NFKC dan NFKD melangkah lebih jauh dan menulis ulang karakter yang hanya tampak serupa — ligatur fi, A lebar penuh, ① berlingkar, angka Romawi Ⅻ. Halaman ini memasukkan teks yang sama ke keempatnya, mencetak satu baris per karakter yang menyatakan apa yang berubah dan mengapa, menghitung grafem terhadap titik kode dan byte UTF-8, serta membandingkan dua string di bawah bentuk yang Anda pilih. Bagian kedua membahas karakter yang sama sekali tidak pernah muncul: spasi nol-lebar, penyambung, kontrol dua arah, pemilih variasi, karakter kontrol C0 dan C1, titik kode area privat, dan separuh pengganti yang rusak, masing-masing dengan posisi dan namanya, serta opsi untuk menghapusnya dari salinan teks.
Semua pemrosesan berlangsung secara lokal di browser — data Anda tidak pernah diunggah ke server mana pun.

Tidak yakin apa itu normalisasi? Normalisasi Unicode dijelaskan dengan bahasa sederhana — mengapa é bisa menjadi satu karakter atau dua, dan mengapa itu merusak login.
Bentuk yang dilaporkan dan dibandingkan:
Bandingkan kedua kotak setelah:
NFC (belum ada)
NFD (belum ada)
NFKC (belum ada)
NFKD (belum ada)
Belum ada yang dibandingkan. Tempel kedua string dan tekan Bandingkan kedua string.
string pertama, dinormalisasi (belum ada)
string kedua, dinormalisasi (belum ada)
Belum ada yang dipindai. Tekan Pindai karakter tak terlihat dan berbahaya, lalu Hapus jika Anda ingin teks tanpa karakter tersebut.
teks dengan kelas yang dipilih dihapus (belum ada)
Ctrl+Enter di kotak pertama menjalankan mode aktif.
Pilih mode dan klik tombol aksinya. Normalkan menulis keempat bentuk teks Anda dan satu baris per karakter; Bandingkan menyatakan apakah dua string sama setelah normalisasi; Pindai mendaftar karakter yang tidak muncul, dan dapat menghapusnya. Tombol Self-test menjalankan ulang setiap kasus yang didokumentasikan dalam berkas data halaman ini, setiap karakter yang disebut tabel tak terlihat dan kontrol, lalu melaporkan jumlahnya di sini.
Setiap karakter, satu baris masing-masing:apa karakternya, apa yang dilakukan keempat bentuk terhadapnya, dan mengapa. Baris yang diberi warna adalah karakter yang diubah oleh setidaknya satu bentuk; kolom NFD menunjukkan apa yang dipecah karakter menjadi saat dipecah.
Berapa banyak karakter itu?Teks yang sama dihitung dengan empat cara: klaster grafem (apa yang dihitung pembaca), titik kode (apa yang ditetapkan Unicode), unit UTF-16 (apa yang diindeks JavaScript), dan byte UTF-8 (apa yang dikirim). Normalisasi memindahkan angka di antara kolom kedua dan ketiga tanpa mengubah yang pertama.
Yang ditemukan, sesuai urutan kemunculannya:posisinya menghitung titik kode, kelasnya adalah kelas tempat karakter itu termasuk, dan catatannya menyatakan apa yang dilakukan karakter itu terhadap apa pun yang membaca teks berikutnya.
Kelas yang diperlakukan halaman ini sebagai mencurigakan:tabel dicetak langsung dari pemindai itu sendiri, sehingga kata-kata di halaman ini dan perilaku tombol tidak dapat berbeda. Tingkat keparahan berkaitan dengan dampak pada perbandingan, bukan seberapa besar kemungkinan karakter itu tidak berbahaya.
Karakter area privat didaftar sebagai mencurigakan karena alasan yang berbeda dari yang lain: mereka memang tampil, hanya saja tidak memiliki makna yang disepakati, sehingga titik kode yang sama adalah gambar yang berbeda di setiap font yang mendefinisikannya.
Seberapa besar masukan yang diterima halaman ini, dan mengapa:normalisasi adalah pembangunan ulang seluruh string, dan halaman ini melakukan beberapa di antaranya ditambah pemindaian dan penghitungan grafem, sehingga biayanya tumbuh secara lurus dengan panjang masukan. Batas di bawah ada karena tabel per karakter digambar di dokumen, dan tabel dengan satu juta baris itulah yang benar-benar menghentikan peramban.
Apa itu normalisasi Unicode?

Unicode memberi lebih dari satu cara untuk menulis beberapa karakter. Huruf é dapat berupa satu titik kode, U+00E9, atau e biasa diikuti tanda aksen kombinasi, U+0065 U+0301. Keduanya tampak sama di layar, tercetak sama, dan sebagai string keduanya berbeda: panjangnya berbeda, byte UTF-8-nya berbeda, dan 'é' === 'é' bernilai false.

Itu bukan keganjilan, melainkan kelas bug. Apakah Anda mendapatkan bentuk yang satu atau yang lain bergantung pada tata letak keyboard, sistem operasi, metode input, dan apakah teks pernah disalin melalui pengolah kata. Pemeriksaan kata sandi, indeks unik basis data, daftar blokir, atau kunci cache yang membandingkan string byte demi byte akan memperlakukan kedua ejaan sebagai dua nilai berbeda — sehingga seorang pengguna dapat menetapkan kata sandi di satu perangkat dan tidak dapat masuk dari perangkat lain, dan kata yang sama dapat muncul dua kali dalam daftar nama "sudah dipakai".

Versi 30 detik: NFC dan NFD adalah teks yang sama yang ditulis dua cara — NFC menggabungkan (e + aksen menjadi satu karakter), NFD mendekomposisi (satu karakter menjadi e + aksen). Keduanya dimaksudkan tanpa kehilangan dan merupakan dua bentuk yang digunakan untuk perbandingan dan penyimpanan. NFKC dan NFKD melakukan semua yang dilakukan dua yang pertama lalu juga mengganti karakter kompatibilitas: ligatur fi menjadi dua huruf, ½ menjadi 1⁄2, angka Romawi ⅲ menjadi XII, A lebar penuh menjadi A biasa. Itulah yang diinginkan indeks pencarian atau filter kata kunci, dan itulah yang tidak boleh pernah diterapkan pada kata sandi.
Mengapa empat bentuk dan bukan satu

Pembagian ini ada karena dua tujuan berbeda sedang dilayani. Menggabung dan memecah teks adalah perubahan kanonik: hasilnya berarti hal yang sama dan, untuk dua bentuk pertama, tidak ada yang hilang. Melipat ligatur, superskrip, huruf berlingkar, dan bentuk lebar penuh adalah perubahan kompatibilitas: dilakukan demi kemudahan pencocokan, dan memang menghancurkan perbedaan antara dua string yang oleh Unicode dinyatakan berbeda. Keempat bentuk adalah setiap kombinasi dari kedua keputusan itu.

Tabel di atas adalah daftar kasus dari berkas data halaman ini, diperiksa terhadap String.prototype.normalize milik platform, dan Self-test memeriksa ulang setiap barisnya di depan Anda. Bacalah dua baris pertama bersama-sama: keduanya adalah teks yang sama, ditulis dengan dua cara, dan setelah normalisasi keduanya adalah string yang sama.

Contoh yang dikerjakan

Ini adalah string yang dilalui tombol Contoh, dinormalisasi secara langsung oleh halaman alih-alih diketik sebagai jawaban. Masing-masing dipilih karena sebuah bentuk melakukan sesuatu yang terlihat padanya.

Karakter kompatibilitas: apa harga NFKC

NFKC dan NFKD bersifat lossy, dan kehilangan itu disengaja. Keduanya menjawab pertanyaan "apakah ini kata yang sama", bukan pertanyaan "apakah ini string yang sama". Tiga konsekuensi mengikuti, dan ketiganya mudah keliru dalam kode produksi:

  • Jangan pernah menyimpan kata sandi, token, atau tanda tangan dalam bentuk kompatibilitas. Dua masukan berbeda akan runtuh menjadi nilai tersimpan yang sama, yang mengubah pemeriksaan panjang atau karakter menjadi lebih lemah. Normalkan kata sandi dengan NFC (atau tidak sama sekali) dan bandingkan sebagai byte setelah melakukan hal yang persis sama di kedua sisi.
  • Lipatan kompatibilitas dapat mengubah satu karakter menjadi beberapa. Ligatur fi adalah satu titik kode yang menjadi dua huruf, sehingga kolom yang divalidasi panjangnya sebelum normalisasi bisa menjadi lebih panjang sesudahnya. Normalkan dulu, lalu validasi, lalu simpan.
  • NFKC tidak menghapus semua yang tak terlihat. Pemilih variasi bertahan melalui NFKC dan NFKD, begitu pula penyambung nol-lebar. Itulah sebabnya halaman ini memiliki mode pemindaian: normalisasi menjawab pertanyaan tentang bentuk dan identitas sebuah karakter, dan pemindai menjawab pertanyaan apakah sesuatu ada di sana sama sekali.
Grafem, titik kode, dan byte

Normalisasi mengubah angka kedua dan biasanya membiarkan yang pertama. Ketiga angka itu semuanya "panjang teks" dan semuanya berbeda, itulah sebabnya bidang formulir dengan batas 20 karakter dapat berisi string yang panjangnya 20 titik kode, 30 unit UTF-16, dan 47 byte.

Angka di kolom grafem adalah apa yang dihitung manusia. Selisihnya dengan jumlah titik kode adalah tepat inti dari pemindai: nama yang terdiri dari 12 titik kode dapat tampil sebagai 12 huruf, sebagai 3 huruf dengan 9 tanda tak terlihat di antaranya, atau sebagai satu emoji.

Karakter yang tidak dapat Anda lihat

Spasi nol-lebar tidak memakan lebar, tidak mengubah glif, dan merupakan karakter sungguhan dengan titik kode sungguhan. Dua pengenal yang tampak identik di layar dapat berbeda satu di antaranya, dan begitulah nama dalam daftar bisa menjadi tiruan yang diurutkan, dicari, dan diautentikasi berbeda dari nama di sebelahnya. Hal yang sama berlaku untuk kontrol dua arah, yang dapat membuat string tampil dalam urutan yang berbeda dari urutan penulisannya — trik yang pernah dipakai untuk menyamarkan nama berkas.

Pemindai pada mode ketiga mendaftar setiap karakter tersebut dengan posisinya: karakter nol-lebar dan penyambung, kontrol dua arah, pemilih variasi, spasi yang tidak biasa, karakter kontrol C0 dan C1, titik kode area privat, dan pengganti yang bukan separuh dari pasangan valid, yang sebenarnya bukan karakter sama sekali. Tabel kelas di atas menjelaskan apa yang dilakukan setiap kelas, dan tombol penghapusan menghapus salinan karakter-karakter itu tanpa menyentuh masukan Anda.

Kesalahan yang dirancang halaman ini untuk ditangkap
  • Membandingkan dua string tanpa menormalkannya. 'é' === 'é' bernilai false. Normalkan kedua sisi dengan bentuk yang sama sebelum membandingkan, dan gunakan bentuk yang sama di indeks yang Anda bandingkan.
  • Menormalkan hanya di satu sisi. Pencarian yang menormalkan kueri tetapi tidak nilai tersimpan tidak menemukan apa pun, dan tampak seperti masalah data, bukan masalah normalisasi.
  • Menata gaya formulir dengan bentuk kompatibilitas lalu menyebutnya aman untuk NFC. NFKC yang diterapkan pada kata sandi mengecilkan ruang kemungkinan kata sandi dan menggabungkan masukan yang seharusnya berbeda.
  • Mengira bentuk normalisasi memperbaiki pemalsuan. Ia tidak menghapus penyambung nol-lebar, pemilih variasi, atau homoglif — а Sirilik dan a Latin adalah dua titik kode berbeda yang dibiarkan oleh NFC.
  • Mempercayai jumlah karakter. Jumlah yang dilihat pengguna, jumlah yang ditegakkan kolom basis data, dan jumlah yang ditegakkan batas byte adalah tiga angka berbeda, dan baris-baris di atas menunjukkan seberapa jauh mereka bisa berbeda.
  • Mengira NFC adalah yang akan Anda dapatkan. Teks yang datang dari metode input, berkas, atau sistem lain berada dalam bentuk apa pun yang dihasilkan sistem itu. Halaman ini dimulai dengan NFC karena itulah bentuk yang diharapkan untuk web, bukan karena itulah masukan Anda.
Jawaban singkat

“Mengapa dua string ini tidak sama padahal tampak sama?”

Karena salah satunya ditulis dengan karakter tergabung dan yang lain dengan huruf dasar ditambah tanda kombinasi, atau salah satunya berisi karakter yang sama sekali tidak menggambar apa pun. Beralih ke mode Bandingkan dan letakkan satu string di setiap kotak: halaman melaporkan posisi pertama tempat string yang dinormalisasi berbeda dan apa yang ada di posisi itu pada masing-masing.

“Bentuk mana yang sebaiknya digunakan dalam aplikasi?”

NFC untuk apa pun yang akan dibaca, ditampilkan, disimpan, dan dicari manusia, yaitu bentuk yang direkomendasikan W3C untuk web. NFD adalah jawaban yang tepat ketika kode di baliknya menginginkan huruf dasar dan tanda terpisah, yang umum dalam pemrosesan teks dan pada beberapa sistem berkas di macOS. Gunakan NFKC atau NFKD hanya ketika Anda mencocokkan, bukan menyimpan: indeks pencarian, filter kata kunci, deteksi duplikat.

“Apakah string yang sudah dinormalisasi masih ternormalisasi setelah digabungkan dengan yang lain?”

Belum tentu. Normalisasi tidak terjaga oleh penggabungan: menggabungkan dua string NFC dapat menghasilkan urutan yang akan ditulis berbeda oleh NFC, karena tanda kombinasi di awal string kedua dapat menempel pada karakter di akhir string pertama. Normalkan setelah perakitan, bukan hanya sebelumnya.

“Apakah normalisasi menghapus karakter tak terlihat?”

Tidak, dengan satu pengecualian parsial: NFKC dan NFKD memang mengganti spasi tanpa jeda dengan spasi biasa, yang merupakan hasil yang terlihat dari lipatan kompatibilitas. Semua yang lain — spasi nol-lebar, penyambung, kontrol dua arah, pemilih variasi — lolos melalui keempat bentuk tanpa tersentuh. Itulah tujuan mode ketiga.

“Mengapa jumlah byte berubah padahal jumlah karakter tidak?”

Karena kedua bentuk dibangun dari titik kode berbeda yang memakan jumlah byte berbeda dalam UTF-8. é prakomposisi adalah dua byte; pasangan terdekomposisi adalah tiga. Tabel jumlah di atas mencetak keempat angka berdampingan untuk masukan dan untuk setiap bentuk, sehingga perbedaannya terlihat, bukan hanya dijelaskan.

“Bagaimana memastikan hasil di halaman ini benar?”

Tekan Self-test. Ia menormalkan setiap kasus dalam berkas data halaman ini, memeriksa ulang keempat bentuk satu sama lain untuk hubungan yang terdokumentasi (NFC bersifat idempoten, NFD dari string NFC sama dengan NFD dari aslinya, dan seterusnya), menjalankan setiap karakter yang disebut dalam tabel tak terlihat dan kontrol melalui pemindai, dan menghitung kasus grafem terhadap Intl.Segmenter.

Standar dan kredit

Keempat bentuk dan definisinya berasal dari Unicode Standard, Annex 15 (Unicode Normalization Forms). Titik kode yang digunakan sebagai contoh dekomposisi adalah yang ditetapkan standar; halaman ini tidak mengimplementasikan tabel dekomposisi sendiri — ia memanggil String.prototype.normalize milik browser, yang merupakan implementasi yang sama dengan yang dipakai platform untuk masukan teks, pengurutan, dan perbandingan, sehingga yang dilaporkan di sini adalah apa yang dilakukan sisa sistem terhadap string yang sama. Klaster grafem berasal dari Intl.Segmenter jika browser memilikinya, dan dari perkiraan yang terdokumentasi jika tidak.

Teks halaman ditulis untuk situs ini. Setiap contoh yang dikerjakan di halaman ini dihasilkan oleh kode halaman itu sendiri lalu diperiksa terhadap daftar kasus yang tercatat di berkas data, yang juga diperiksa terhadap data NormalizationTest Unicode untuk karakter yang sama. Tabel karakter tak terlihat dan berbahaya adalah subset yang dikurasi yang ditulis untuk peringatan yang dapat dibaca manusia, bukan profil keamanan Unicode lengkap (UTS #39), dan halaman mengatakannya alih-alih menyiratkan kelengkapan yang tidak dimilikinya.