1. Pendahuluan
Bahkan, sejak saya mulai menulis kode Java, saya telah mengalami masalah yang tak terhitung jumlahnya dan transcoding, seperti kode kacau yang terjadi ketika membaca dari file teks ke dalam string, kode kacau yang terjadi ketika mendapatkan parameter permintaan HTTP dalam servlet, kode yang kacau yang terjadi ketika ditanyakan oleh JDBC, dll. Masalah ini sangat umum. Saat Anda bertemu mereka, Anda dapat menyelesaikannya dengan sukses dengan mencari mereka, sehingga Anda tidak memiliki pemahaman yang mendalam.
Sampai dua hari yang lalu, teman sekelas saya berbicara kepada saya tentang masalah pengkodean file sumber Java (masalah ini dianalisis dalam contoh terakhir), dan dimulai dengan masalah ini dan dimulai dengan serangkaian masalah. Kemudian kami membahas saat mencari informasi. Sudah larut malam bahwa kami akhirnya menemukan petunjuk kunci dalam sebuah blog, memecahkan semua keraguan, dan kalimat yang tidak kami pahami sebelumnya dapat dijelaskan dengan jelas. Oleh karena itu, saya memutuskan untuk menggunakan esai ini untuk merekam pemahaman saya tentang beberapa masalah pengkodean dan hasil percobaan.
Beberapa konsep berikut adalah pemahaman saya sendiri berdasarkan kondisi aktual. Jika ada kesalahan, pastikan untuk memperbaikinya.
2. Ringkasan Konsep
Pada hari -hari awal, Internet belum berkembang, dan komputer hanya digunakan untuk memproses beberapa data lokal, begitu banyak negara dan wilayah yang merancang skema pengkodean untuk bahasa lokal. Pengkodean terkait regional semacam ini secara kolektif disebut pengkodean ANSI (karena mereka adalah ekstensi untuk kode ANSI-ASCII). Namun, mereka tidak membahas terlebih dahulu bagaimana menjadi kompatibel satu sama lain, tetapi sebaliknya melakukannya sendiri, yang meletakkan akar dari konflik pengkodean. Misalnya, pengkodean GB2312 yang digunakan dalam konflik daratan dengan pengkodean BIG5 yang digunakan di Taiwan. Dua byte yang sama mewakili karakter yang berbeda dalam dua skema pengkodean. Dengan munculnya internet, sebuah dokumen sering berisi berbagai bahasa, dan komputer mengalami masalah saat menampilkannya karena tidak tahu mana yang mengkode kedua byte ini.
Masalah seperti itu adalah umum di dunia, jadi seruan untuk mendefinisikan kembali set karakter umum dan penomoran terpadu semua karakter di dunia meningkat.
Akibatnya, kode Unicode muncul, itu secara seragam memberi nomor semua karakter di dunia. Karena dapat mengidentifikasi karakter secara unik, font hanya perlu dirancang untuk kode unicode. Namun, standar Unicode mendefinisikan set karakter, tetapi tidak menentukan skema pengkodean, yaitu, hanya mendefinisikan angka abstrak dan karakter yang sesuai, tetapi tidak menentukan cara menyimpan string angka unicode. Persyaratan sebenarnya adalah cara menyimpan UTF-8, UTF-16, UTF-32 dan solusi lainnya. Oleh karena itu, pengkodean dengan awal UTF dapat secara langsung dikonversi melalui perhitungan dan nilai unicode (codepoints, titik kode). Seperti namanya, UTF-8 adalah pengkodean panjang 8-bit, yang merupakan pengkodean panjang variabel, menggunakan 1 hingga 6 byte untuk menyandikan karakter (karena dibatasi oleh rentang unicode, sebenarnya hanya 4 byte paling banyak); UTF-16 adalah penyandian unit dasar 16-bit, yang juga merupakan pengkodean panjang variabel, baik 2 byte atau 4 byte; UTF-32 adalah panjang tetap, dan 4 byte tetap menyimpan nomor unicode.
Sebenarnya, saya selalu sedikit salah paham tentang Unicode sebelumnya. Dalam kesan saya, kode Unicode hanya dapat mencapai 0xFFFF, yang berarti hanya dapat mewakili hingga 2^16 karakter. Setelah dengan cermat membaca Wikipedia, saya menyadari bahwa skema pengkodean UCS-2 awal memang seperti ini. UCS-2 menggunakan dua byte untuk menyandikan karakter, sehingga hanya dapat menyandikan karakter dalam kisaran BMP (bidang multibahasa dasar, yaitu, 0x0000-0xffff, yang berisi karakter yang paling umum digunakan di dunia). Untuk mengkodekan karakter dengan unicode lebih besar dari 0xFFFF, orang telah memperluas encoding UCS-2 dan membuat pengkodean UTF-16, yang merupakan panjang variabel. Dalam rentang BMP, UTF-16 persis sama dengan UCS-2, sementara UTF-16 di luar BMP menggunakan 4 byte untuk disimpan.
Untuk memfasilitasi deskripsi di bawah ini, izinkan saya menjelaskan konsep unit kode (CodeUnit). Komponen dasar dari pengkodean tertentu disebut unit kode. Misalnya, unit kode UTF-8 adalah 1 byte, dan unit kode UTF-16 adalah 2 byte. Sulit untuk dijelaskan, tetapi mudah dimengerti.
Agar kompatibel dengan berbagai bahasa dan cross-platform yang lebih baik, Javastring menyimpan kode unicode untuk karakter. Digunakan untuk menggunakan skema pengkodean UCS-2 untuk menyimpan Unicode. Kemudian, ditemukan bahwa karakter dalam kisaran BMP tidak cukup, tetapi untuk konsumsi memori dan pertimbangan kompatibilitas, itu tidak naik ke UCS-4 (mis. UTF-32, pengkodean 4-byte tetap), tetapi mengadopsi UTF-16 yang disebutkan di atas. Jenis char dapat dianggap sebagai unit kodenya. Praktek ini menyebabkan beberapa masalah. Jika semua karakter berada dalam kisaran BMP, tidak apa -apa. Jika ada karakter di luar BMP, itu bukan lagi unit kode yang sesuai dengan karakter. Metode panjang mengembalikan jumlah unit kode, bukan jumlah karakter. Metode ChARAT secara alami mengembalikan unit kode alih -alih karakter, yang menjadi merepotkan saat melintasi. Meskipun beberapa metode operasi baru disediakan, itu masih tidak nyaman dan tidak dapat diakses secara acak.
Selain itu, saya menemukan bahwa Java tidak memproses unicode literal yang lebih besar dari 0xFFFF ketika dikompilasi, jadi jika Anda tidak dapat mengetikkan karakter non-BMP, tetapi Anda tahu kode unicode, Anda harus menggunakan metode yang relatif bodoh untuk membiarkan string menyimpannya: menghitung nilainya secara manual. Kode sampel adalah sebagai berikut.
public static void main (string [] args) {// string str = ""; // Kami ingin menetapkan karakter seperti itu, dengan asumsi bahwa metode input saya tidak dapat diketik // tetapi saya tahu bahwa unicode -nya adalah 0x1d11e // string str = "/u1d11e"; // Ini tidak akan dikenali // sehingga dapat dihitung melalui pengkodean UTF-16 D834 dd1estring str = "/ud834/udd1e"; // kemudian tulis system.out.println (str); // berhasil output ""}Notepad yang dilengkapi dengan Windows dapat disimpan sebagai pengkodean unicode, yang sebenarnya mengacu pada pengkodean UTF-16. Seperti disebutkan di atas, pengkodean karakter utama yang digunakan semuanya berada dalam kisaran BMP, dan dalam rentang BMP, nilai pengkodean UTF-16 dari setiap karakter sama dengan nilai unicode yang sesuai, yang mungkin mengapa Microsoft menyebutnya Unicode. Sebagai contoh, saya memasukkan "Good A" dua karakter di Notepad, dan kemudian menyimpannya sebagai penyandian Unicode Big Endian (prioritas bit tinggi), dan membuka file dengan WinHex. Konten seperti yang ditunjukkan pada gambar di bawah ini. Dua byte pertama dari file ini disebut tanda pesanan byte (tanda pesanan byte), (FF FF) menandai urutan endian sebagai prioritas bit tinggi, dan kemudian (59 7d) adalah kode unicode "baik", dan (00 61) adalah kode unicode "A".
Dengan kode unicode, masalahnya tidak dapat diselesaikan segera, karena pertama-tama, ada sejumlah besar data pengkodean standar non-unicode di dunia, dan tidak mungkin bagi kita untuk membuangnya. Kedua, pengkodean unicode sering kali membutuhkan lebih banyak ruang daripada pengkodean ANSI, jadi dari perspektif sumber daya menghemat, pengkodean ANSI masih diperlukan. Oleh karena itu, perlu untuk menetapkan mekanisme konversi sehingga pengkodean ANSI dapat dikonversi ke Unicode untuk pemrosesan terpadu, atau unicode dapat dikonversi ke pengkodean ANSI untuk memenuhi persyaratan platform.
Metode konversi relatif mudah untuk dikatakan. Untuk Seri UTF atau ISO-8859-1, penyandian yang kompatibel dapat secara langsung dikonversi melalui perhitungan dan nilai unicode (pada kenyataannya, mungkin juga pencarian tabel). Untuk pengkodean ANSI yang tersisa dari sistem, itu hanya dapat dilakukan dengan mencari meja. Microsoft memanggil tabel pemetaan ini Codepage (halaman kode) dan mengklasifikasikan dan diberi nomor dengan pengkodean. Misalnya, CP936 umum kami adalah halaman kode GBK, dan CP65001 adalah halaman kode UTF-8. Gambar berikut adalah tabel pemetaan GBK-> Unicode yang ditemukan di situs web resmi Microsoft (secara visual tidak lengkap). Demikian pula, harus ada tabel pemetaan Unicode-> GBK terbalik.
Dengan halaman kode, Anda dapat dengan mudah melakukan berbagai konversi pengkodean. Misalnya, mengonversi dari GBK ke UTF-8, Anda hanya perlu membagi data berdasarkan karakter sesuai dengan aturan pengkodean GBK, menggunakan data yang dikodekan dari setiap karakter untuk memeriksa halaman kode GBK, mendapatkan nilai Unicode, dan kemudian menggunakan Unicode untuk memeriksa Halaman Kode UTF-8 (atau secara langsung. Hal yang sama berlaku untuk sebaliknya. Catatan: UTF-8 adalah implementasi standar dari Unicode. Halaman kodenya berisi semua nilai Unicode, sehingga pengkodean apa pun dikonversi ke UTF-8 dan kemudian dikonversi tidak akan hilang. Pada titik ini, kita dapat menarik kesimpulan bahwa untuk menyelesaikan pekerjaan konversi pengkodean, yang paling penting adalah dengan sukses mengonversi ke Unicode, jadi dengan benar memilih set karakter (halaman kode) adalah kuncinya.
Setelah memahami sifat masalah kehilangan transkode, saya tiba-tiba memahami mengapa kerangka kerja JSP menggunakan ISO-8859-1 untuk mendekode parameter permintaan HTTP, yang mengarah pada fakta bahwa kami harus menulis pernyataan seperti itu ketika kami mendapatkan parameter Cina:
Stringparam=newString(s.getBytes("iso-8859-1"),"UTF-8");
Karena kerangka kerja JSP menerima aliran byte biner yang dikodekan oleh parameter, ia tidak tahu apa yang menyandikannya (atau tidak peduli), dan tidak tahu halaman kode mana yang harus diperiksa untuk dikonversi ke Unicode. Kemudian ia memilih solusi yang tidak akan pernah menyebabkan kerugian. Diasumsikan bahwa ini adalah data yang dikodekan oleh ISO-8859-1, dan kemudian mencari halaman kode ISO-8859-1 untuk mendapatkan urutan Unicode. Karena ISO-8859-1 dikodekan oleh byte, dan tidak seperti ASCII, ia mengkodekan setiap bit ruang 0 ~ 255, sehingga setiap byte dapat ditemukan di halaman kodenya. Jika diputar dari Unicode ke aliran byte asli, tidak akan ada kerugian. Dengan cara ini, untuk programmer Eropa dan Amerika yang tidak mempertimbangkan bahasa lain, mereka dapat secara langsung memecahkan kode string dengan kerangka kerja JSP. Jika mereka ingin kompatibel dengan bahasa lain, mereka hanya perlu kembali ke aliran byte asli dan memecahkan kode dengan halaman kode yang sebenarnya.
Saya telah selesai menjelaskan konsep terkait unicode dan pengkodean karakter. Selanjutnya, saya akan menggunakan contoh Java untuk mengalaminya.
AKU AKU AKU. Analisis contoh
1. Konversi ke konstruktor unicode-string
Metode pembuatan string adalah untuk mengubah berbagai data yang dikodekan menjadi urutan unicode (disimpan dalam pengkodean UTF-16). Kode pengujian berikut digunakan untuk menunjukkan aplikasi metode konstruksi Javastring. Karakter non-BMP terlibat dalam contoh, jadi metode codepointat tidak digunakan.
Tes Kelas Publik {public static void main (string [] args) melempar ioException {// "hello" gbk encoded data byte [] gbkdata = {(byte) 0xc4, (byte) 0xe3, (byte) 0xba, (byte) 0xc3};/ hello = hello = hello = hello = hello "hello = hello" hello "hello" hello) 0xba [byte) 0xc3}; {(byte)0xa7, (byte)0x41, (byte)0xa6, (byte)0x6e};//Construct String and decode it to UnicodeString strFromGBK = new String(gbkData, "GBK");String strFromBig5 = new String(big5Data, "BIG5");//Output Unicode sequences respectively showunicode (strFromGBK); showUnicode (strfrombig5);} public static void showUnicode (string str) {for (int i = 0; i <str.length (); i ++) {System.out.printf ("// u%x", (int) str.charat (i));} System.out.println ();}};Hasil operasi adalah sebagai berikut
Dapat ditemukan bahwa karena string Masters Unicode Code, itu perlu dikonversi ke pengkodean lain Soeasy!
3. Menggunakan Unicode sebagai jembatan untuk mewujudkan pengkodean konversi bersama
Dengan fondasi dua bagian di atas, sangat mudah untuk mewujudkan pengkodean dan konversi timbal balik. Anda hanya perlu menggunakannya bersama. Pertama, Newstring mengubah data yang dikodekan asli menjadi urutan unicode, dan kemudian memanggil GetBytes untuk ditransfer ke pengkodean yang ditentukan.
Misalnya, kode konversi GBK ke BIG5 yang sangat sederhana adalah sebagai berikut
public static void main(String[] args) throws UnsupportedEncodingException {//Suppose this is the data read from the file in a byte stream (GBK encoding) byte[] gbkData = {(byte) 0xc4, (byte) 0xe3, (byte) 0xba, (byte) 0xc3};//Convert to UnicodeString tmp = new String (gbkdata, "gbk"); // konversi dari unicode ke byte byte BIG5 [] Big5Data = tmp.getbytes ("BIG5"); // Operasi Kedua ...}4. Masalah kerugian pengkodean
Seperti yang dijelaskan di atas, alasan mengapa kerangka kerja JSP menggunakan karakter ISO-8859-1 yang ditetapkan untuk memecahkan kode. Pertama gunakan contoh untuk mensimulasikan proses pemulihan ini, kodenya adalah sebagai berikut
public class Test {public static void main(String[] args) throws UnsupportedEncodingException {//JSP framework receives 6 bytes of data byte[] data = {(byte) 0xe4, (byte) 0xbd, (byte) 0xa0, (byte) 0xe5, (byte) 0xa5, (byte) 0xbd};//Print the original data showbytes (data); // JSP Kerangka kerja mengasumsikan bahwa itu adalah pengkodean ISO-8859-1, menghasilkan string string string tmp = string baru (data, "iso-8859-1"); // ************************ // setelah pengembang, itu dicetak dan dicetak. Hasil decoding: " + tmp); // Jadi pertama-tama dapatkan 6 byte data asli (lihatlah halaman kode ISO-8859-1) yang terbalik [] utfdata = tmp.getbytes (" iSo-8859-1 "); // cetak data yang dipulihkan, utus. UTF-8 untuk merekonstruksi hasil string string string = string baru (utfdata, "utf-8"); // cetak lagi, itu benar! System.out.println ("UTF-8 Hasil Decoding:" + Hasil);} public static void showbytes (byte [] data) {for (byte b: data) system.out.printf ("0x%x", b); System.out.println ();}}Hasil berjalan adalah sebagai berikut. Output pertama salah karena aturan decoding salah. Saya juga memeriksa halaman kode secara tidak benar dan mendapat unicode yang salah. Kemudian saya menemukan bahwa data dapat dipulihkan dengan sempurna melalui pemeriksaan kembali Unicode yang salah dari halaman kode ISO-8859-1.
Ini bukan intinya. Jika kuncinya adalah menggantikan "Cina" dengan "Cina", kompilasi akan berhasil, dan hasil operasinya seperti yang ditunjukkan pada gambar di bawah ini. Selain itu, dapat ditemukan lebih lanjut bahwa ketika jumlah karakter Cina ganjil, kompilasi gagal dan ketika jumlahnya bahkan, ia lewat. Mengapa ini? Mari kita analisis secara rinci di bawah ini.
Karena Javastring menggunakan Unicode secara internal, kompiler akan mentranskode string literal kami selama kompilasi dan mengonversi dari pengkodean file sumber ke Unicode (Wikipedia mengatakan ia menggunakan pengkodean yang sedikit berbeda dari UTF-8). Saat menyusun, kami tidak menentukan parameter penyandian, sehingga kompiler akan memecahkan kode di GBK secara default. Jika Anda memiliki pengetahuan tentang UTF-8 dan GBK, Anda harus tahu bahwa umumnya karakter Cina membutuhkan 3 byte untuk menggunakan pengkodean UTF-8, sementara GBK hanya membutuhkan 2 byte. Ini dapat menjelaskan mengapa paritas nomor karakter akan mempengaruhi hasilnya, karena jika ada 2 karakter, pengkodean UTF-8 menempati 6 byte, dan decoding dalam GBK dapat didekodekan menjadi 3 karakter. Jika itu adalah 1 karakter, akan ada byte yang tidak dapat diubah, yang merupakan tempat di mana tanda tanya pada gambar.
Agar lebih spesifik, pengkodean UTF-8 dari kata "Cina" dalam file sumber adalah E4B8ade59BBD. Kompiler mendekodenya di GBK. Pasangan 3 byte mencari cp936 untuk mendapatkan 3 nilai unicode, yang masing -masing 6D93E15E6D57, sesuai dengan tiga karakter aneh dalam grafik hasil. Seperti yang ditunjukkan pada gambar di bawah ini, setelah kompilasi, ketiga unicode ini sebenarnya disimpan dalam pengkodean seperti UTF-8 dalam file .class. Saat berjalan, Unicode disimpan di JVM. Namun, ketika output akhir adalah output, masih akan dikodekan dan diteruskan ke terminal. Pengkodean yang disepakati kali ini adalah pengkodean yang ditetapkan oleh area sistem, jadi jika pengaturan pengkodean terminal diubah, itu masih akan kacau. E15E kami di sini tidak menentukan karakter yang sesuai dalam standar Unicode, sehingga layar akan berbeda di bawah font yang berbeda pada platform yang berbeda.
Dapat dibayangkan bahwa jika file sumber disimpan dalam pengkodean GBK, dan kemudian menipu kompiler untuk mengatakan itu adalah UTF-8, pada dasarnya tidak dapat dikompilasi dan dilewati tidak peduli berapa banyak karakter Cina yang dimasukkan, karena penyandian UTF-8 sangat teratur, dan byte gabungan secara acak tidak akan sesuai dengan aturan UTF-8.
Tentu saja, cara paling langsung untuk memungkinkan kompiler untuk mengonversi pengkodean ke unicode dengan benar adalah dengan jujur memberi tahu kompiler apa pengkodean file sumber.
4. Ringkasan
Setelah koleksi dan percobaan ini, saya belajar banyak konsep terkait dengan pengkodean dan menjadi terbiasa dengan proses spesifik konversi pengkodean. Ide -ide ini dapat digeneralisasi ke berbagai bahasa pemrograman, dan prinsip -prinsip implementasinya serupa. Jadi saya pikir saya tidak akan lagi mengetahui masalah semacam ini di masa depan.
Di atas adalah semua konten dari artikel ini tentang contoh konsep pengkodean seperti ANSI, Unicode, BMP, UTF, dll. Saya harap ini akan membantu semua orang. Teman yang tertarik dapat terus merujuk ke topik terkait lainnya di situs ini. Jika ada kekurangan, silakan tinggalkan pesan untuk menunjukkannya. Terima kasih teman atas dukungan Anda untuk situs ini!