Ada dua tahap untuk memproses teks Unicode. Yang pertama adalah "bagaimana saya bisa memasangnya dan mengeluarkannya tanpa kehilangan informasi". Yang kedua adalah "bagaimana saya memperlakukan teks sesuai dengan konvensi bahasa lokal".
Posting tchrist mencakup keduanya, tetapi bagian kedua adalah dari mana 99% teks dalam posnya berasal. Sebagian besar program bahkan tidak menangani I / O dengan benar, jadi penting untuk memahami bahwa sebelum Anda mulai khawatir tentang normalisasi dan penyatuan.
Posting ini bertujuan untuk menyelesaikan masalah pertama itu
Ketika Anda membaca data ke Perl, tidak peduli apa itu pengkodean. Ini mengalokasikan beberapa memori dan menyimpan byte di sana. Jika Anda mengatakanprint $str
, itu hanya mengeluarkan byte itu ke terminal Anda, yang mungkin diatur untuk menganggap semua yang ditulis untuk itu adalah UTF-8, dan teks Anda muncul.
Menakjubkan.
Kecuali, tidak. Jika Anda mencoba memperlakukan data sebagai teks, Anda akan melihat bahwa Sesuatu yang Buruk sedang terjadi. Anda tidak perlu melangkah lebih jauh daripada length
melihat bahwa apa yang dipikirkan Perl tentang string Anda dan apa yang Anda pikirkan tentang string Anda tidak setuju. Tulis one-liner seperti: perl -E 'while(<>){ chomp; say length }'
dan ketikkan ๆๅญๅใ
dan Anda dapatkan 12 ... bukan jawaban yang benar, 4.
Itu karena Perl menganggap string Anda bukan teks. Anda harus mengatakan bahwa itu adalah teks sebelum akan memberikan jawaban yang tepat.
Itu cukup mudah; modul Encode memiliki fungsi untuk melakukan itu. Titik masuk umum adalah Encode::decode
(atauuse Encode qw(decode)
, tentu saja). Fungsi itu mengambil beberapa string dari dunia luar (apa yang kita sebut "oktet", yang suka mengatakan "8-bit byte"), dan mengubahnya menjadi beberapa teks yang Perl akan mengerti. Argumen pertama adalah nama pengkodean karakter, seperti "UTF-8" atau "ASCII" atau "EUC-JP". Argumen kedua adalah string. Nilai kembali adalah skalar Perl yang berisi teks.
(Ada juga Encode::decode_utf8
, yang mengasumsikan UTF-8 untuk pengkodean.)
Jika kita menulis ulang one-liner kita:
perl -MEncode=decode -E 'while(<>){ chomp; say length decode("UTF-8", $_) }'
Kami mengetikkan ๆๅญ ๅ ใ dan mendapatkan "4" sebagai hasilnya. Keberhasilan.
Itu, di sana, adalah solusi untuk 99% masalah Unicode di Perl.
Kuncinya adalah, setiap kali ada teks masuk ke program Anda, Anda harus memecahkan kode itu. Internet tidak dapat mengirim karakter. File tidak dapat menyimpan karakter. Tidak ada karakter di database Anda. Hanya ada oktet, dan Anda tidak bisa memperlakukan oktet sebagai karakter dalam Perl. Anda harus mendekode oktet yang disandikan menjadi karakter Perl dengan modul Encode.
Setengah dari masalah lainnya adalah mengeluarkan data dari program Anda. Itu mudah; Anda hanya mengatakan use Encode qw(encode)
, tentukan pengkodean data Anda nantinya (UTF-8 ke terminal yang memahami UTF-8, UTF-16 untuk file di Windows, dll.), lalu output hasil encode($encoding, $data)
alih-alih hanya menghasilkan $data
.
Operasi ini mengubah karakter Perl, yang menjadi tujuan program Anda, menjadi oktet yang dapat digunakan oleh dunia luar. Akan jauh lebih mudah jika kita bisa mengirim karakter melalui Internet atau ke terminal kita, tetapi kita tidak bisa: oktet saja. Jadi kita harus mengonversi karakter menjadi oktet, jika tidak hasilnya tidak akan ditentukan.
Untuk meringkas: encode semua output dan decode semua input.
Sekarang kita akan berbicara tentang tiga masalah yang membuat ini sedikit menantang. Yang pertama adalah perpustakaan. Apakah mereka menangani teks dengan benar? Jawabannya adalah ... mereka mencoba. Jika Anda mengunduh halaman web, LWP akan mengembalikan hasil Anda sebagai teks. Jika Anda memanggil metode yang tepat pada hasilnya, yaitu (dan itu kebetulan decoded_content
, bukan content
, yang hanya aliran oktet yang didapat dari server.) Driver database dapat bersisik; jika Anda menggunakan DBD :: SQLite hanya dengan Perl, itu akan berhasil, tetapi jika beberapa alat lain telah menempatkan teks disimpan sebagai beberapa penyandian selain UTF-8 dalam database Anda ... well ... itu tidak akan ditangani dengan benar sampai Anda menulis kode untuk menanganinya dengan benar.
Meng-output data biasanya lebih mudah, tetapi jika Anda melihat "karakter luas dicetak", maka Anda tahu Anda mengacaukan penyandian di suatu tempat. Peringatan itu berarti "hei, Anda mencoba membocorkan karakter Perl ke dunia luar dan itu tidak masuk akal". Program Anda tampaknya berfungsi (karena ujung lainnya biasanya menangani karakter Perl mentah dengan benar), tetapi sangat rusak dan dapat berhenti berfungsi kapan saja. Perbaiki dengan eksplisit Encode::encode
!
Masalah kedua adalah kode sumber UTF-8 yang dikodekan. Kecuali Anda mengatakan use utf8
di bagian atas setiap file, Perl tidak akan menganggap bahwa kode sumber Anda adalah UTF-8. Ini berarti bahwa setiap kali Anda mengatakan sesuatu seperti my $var = 'ใปใ'
, Anda memasukkan sampah ke dalam program Anda yang benar-benar akan menghancurkan segalanya. Anda tidak harus "menggunakan utf8", tetapi jika tidak, Anda tidak boleh menggunakan karakter non-ASCII dalam program Anda.
Masalah ketiga adalah bagaimana Perl menangani Masa Lalu. Dahulu kala, tidak ada yang namanya Unicode, dan Perl berasumsi bahwa semuanya adalah teks Latin-1 atau biner. Jadi ketika data masuk ke program Anda dan Anda mulai memperlakukannya sebagai teks, Perl memperlakukan setiap oktet sebagai karakter Latin-1. Itu sebabnya, ketika kami meminta panjang "ๆๅญ ๅ ใ", kami mendapat 12. Perl menganggap bahwa kami beroperasi pada string Latin-1 "รฆรฅรฅรฃ" (yang terdiri dari 12 karakter, beberapa di antaranya non-cetak).
Ini disebut "pemutakhiran implisit", dan itu adalah hal yang sangat masuk akal untuk dilakukan, tetapi bukan itu yang Anda inginkan jika teks Anda bukan Latin-1. Itu sebabnya sangat penting untuk secara eksplisit mendekodekan input: jika Anda tidak melakukannya, Perl akan melakukannya, dan itu mungkin salah.
Orang-orang mengalami masalah di mana setengah data mereka adalah string karakter yang tepat, dan beberapa masih bersifat biner. Perl akan menginterpretasikan bagian yang masih biner seolah-olah itu teks Latin-1 dan kemudian menggabungkannya dengan data karakter yang benar. Ini akan membuatnya terlihat seperti menangani karakter Anda dengan benar merusak program Anda, tetapi pada kenyataannya, Anda belum cukup memperbaikinya.
Berikut ini sebuah contoh: Anda memiliki program yang membaca file teks UTF-8-encoded, Anda menempelkan Unicode PILE OF POO
ke setiap baris, dan Anda mencetaknya. Anda menulis seperti:
while(<>){
chomp;
say "$_ ๐ฉ";
}
Dan kemudian jalankan beberapa data yang disandikan UTF-8, seperti:
perl poo.pl input-data.txt
Ini mencetak data UTF-8 dengan kotoran di akhir setiap baris. Sempurna, program saya berfungsi!
Tapi tidak, Anda hanya melakukan penggabungan biner. Anda membaca oktet dari file, menghapus \n
chomp dengan, dan kemudian menempelkan byte dalam representasi PILE OF POO
karakter UTF-8 . Ketika Anda merevisi program Anda untuk memecahkan kode data dari file dan menyandikan output, Anda akan melihat bahwa Anda mendapatkan sampah ("รฐ ยฉ") bukan kotoran. Ini akan membuat Anda percaya bahwa decoding file input adalah hal yang salah untuk dilakukan. Ini bukan.
Masalahnya adalah bahwa kotoran secara implisit ditingkatkan sebagai latin-1. Jika Anda use utf8
membuat teks literal alih-alih biner, maka teks itu akan berfungsi lagi!
(Itulah masalah nomor satu yang saya lihat ketika membantu orang dengan Unicode. Mereka melakukan bagian yang benar dan itu merusak program mereka. Itulah yang menyedihkan tentang hasil yang tidak ditentukan: Anda dapat memiliki program kerja untuk waktu yang lama, tetapi ketika Anda mulai memperbaikinya, itu rusak. Jangan khawatir, jika Anda menambahkan pernyataan encode / decode ke program Anda dan itu rusak, itu berarti Anda memiliki lebih banyak pekerjaan yang harus dilakukan. Lain kali, ketika Anda merancang dengan Unicode dalam pikiran dari awal, itu akan menjadi jauh lebih mudah!)
Itu benar-benar semua yang perlu Anda ketahui tentang Perl dan Unicode. Jika Anda memberi tahu Perl apa data Anda, itu memiliki dukungan Unicode terbaik di antara semua bahasa pemrograman populer. Namun, jika Anda menganggapnya secara ajaib akan tahu jenis teks apa yang Anda masukkan, maka Anda akan membuang data Anda tanpa bisa dibatalkan. Hanya karena program Anda bekerja hari ini di terminal UTF-8 Anda tidak berarti itu akan berfungsi besok pada file yang dikodekan UTF-16. Jadi amankan sekarang, dan selamatkan diri Anda dari pusing merusak data pengguna Anda!
Bagian yang mudah dari penanganan Unicode adalah menyandikan keluaran dan mendekode masukan. Bagian yang sulit adalah menemukan semua input dan output Anda, dan menentukan pengkodean mana. Tapi itu sebabnya Anda mendapatkan banyak uang :)