Pertama-tama, izinkan saya menjelaskan bahwa ini mengacu pada String di Java Meskipun saya telah memutuskan untuk beralih ke C/C++, karena saya mengalami masalah hari ini, saya masih ingin melihatnya. Definisi String adalah sebagai berikut:
Copy kode kodenya sebagai berikut:
String kelas akhir publik
{
nilai karakter akhir pribadi[]; // string yang disimpan
offset int akhir pribadi; // posisi awal
jumlah int akhir pribadi; //Jumlah karakter
hash int pribadi; // nilai hash yang di-cache
...
}
Saat melakukan debug, Anda dapat melihat nilai yang disimpan sebagai berikut:
Perlu dicatat bahwa jika hashCode() belum dipanggil, nilai hashnya adalah 0. Sangat mudah untuk mengetahui bahwa nilai di sini adalah array char dari nilai string sebenarnya yang disimpan (yaitu, "tes string"), dan berapa nilai setiap karakter? Mudah diverifikasi: Unicode.
Pada titik ini, semua orang dapat menebak bagaimana subString yang biasa kita gunakan diimplementasikan: Jika kita mengimplementasikannya, biarkan String baru menggunakan nilai yang sama (array char) dan hanya ubah offset dan hitungannya. Ini menghemat ruang dan cepat (tidak perlu menyalin), dan kenyataannya seperti ini:
Copy kode kodenya sebagai berikut:
substring String publik(int startIndex) {
mengembalikan substring(beginIndex, count);
}
substring String publik(int indeks awal, int indeks akhir) {
...
kembali ((beginIndex == 0) && (endIndex == hitung)) ?
String baru (offset + beginIndex, endIndex - beginIndex, nilai);
}
String(int offset, int hitungan, nilai char[]) {
this.nilai = nilai;
this.offset = offset;
this.count = hitungan;
}
Karena kita membahas string, pengkodean apa yang digunakan JVM secara default? Melalui debugging Anda dapat menemukan:
Copy kode kodenya sebagai berikut:
Charset statis publik defaultCharset() {
jika (defaultCharset == null) {
disinkronkan (Charset.kelas) {
java.security.PrivilegedAction pa = new GetPropertyAction("file.encoding");
String csn = (String)AccessController.doPrivileged(pa);
Kumpulan karakter cs = pencarian(csn);
jika (cs != nol)
defaultCharset = cs;
kalau tidak
defaultCharset = forName("UTF-8");
}
}
Nilai defaultCharset dapat diteruskan:
-Dfile.encoding=utf-8
Buat pengaturan. Tentu saja, jika Anda ingin menyetelnya ke "abc", Anda bisa, tetapi secara default akan disetel ke UTF-8. Anda dapat melihat nilai spesifik melalui System.getProperty("file.encoding"). Mengapa Anda melihat defaultCharset? Karena proses transmisi jaringan harus berupa array byte, array byte yang diperoleh dengan metode pengkodean berbeda mungkin berbeda. Jadi, kita perlu tahu bagaimana cara pengkodeannya, bukan? Metode spesifik untuk mendapatkan array byte adalah getBytes, yang akan kita fokuskan di bawah. Yang pada akhirnya dipanggil adalah metode encode CharsetEncoder, sebagai berikut:
Copy kode kodenya sebagai berikut:
penyandian CoderResult akhir publik (CharBuffer masuk, ByteBuffer keluar, boolean endOfInput) {
int keadaan baru = endOfInput ?
if ((negara bagian != ST_RESET) && (negara bagian != ST_CODING) && !(endOfInput && (negara bagian == ST_END)))
throwIllegalStateException(negara bagian, negara bagian baru);
negara bagian = negara bagian baru;
untuk (;;) {
Hasil Pembuat Kode cr;
mencoba {
cr = encodeLoop(masuk, keluar);
} tangkapan (BufferUnderflowException x) {
melempar CoderMalfunctionError(x);
} tangkapan (BufferOverflowException x) {
melempar CoderMalfunctionError(x);
}
jika (cr.isOverflow())
kembalikan cr;
jika (cr.isUnderflow()) {
if (endOfInput && in.hasRemaining()) {
cr = CoderResult.malformedForLength(dalam.sisa());
} kalau tidak {
kembalikan cr;
}
}
Tindakan CodingErrorAction = null;
jika (cr.isMalformed())
tindakan = salahInputAction;
lain jika (cr.isUnmappable())
tindakan = Tindakan Karakter yang tidak dapat dipetakan;
kalau tidak
menegaskan salah : cr.toString();
if (aksi == CodingErrorAction.LAPORAN)
kembalikan cr;
if (tindakan == CodingErrorAction.REPLACE) {
if (keluar.sisa() < penggantian.panjang)
kembalikan CoderResult.OVERFLOW;
out.put(pengganti);
}
if ((aksi == CodingErrorAction.IGNORE) || (aksi == CodingErrorAction.REPLACE)) {
di.posisi(dalam.posisi() + cr.panjang());
melanjutkan;
}
menyatakan salah;
}
}
Tentu saja, CharsetEncoder yang sesuai akan dipilih terlebih dahulu sesuai dengan format pengkodean yang diperlukan, dan yang paling penting adalah CharsetEncoder yang berbeda mengimplementasikan metode encodeLoop yang berbeda. Anda mungkin tidak mengerti mengapa ada for(;;) di sini? Faktanya, Anda dapat memahaminya secara kasar dengan melihat paket (nio) tempat CharsetEncoder berada dan parameternya: fungsi ini dapat menangani aliran (walaupun kami tidak akan melakukan perulangan saat menggunakannya di sini).
Dalam metode encodeLoop, sebanyak mungkin karakter akan diubah menjadi byte, dan String baru hampir merupakan proses kebalikan dari di atas.
Dalam proses pengembangan sebenarnya sering dijumpai karakter-karakter yang kacau:
Dapatkan nama file saat mengunggah file;
String diteruskan oleh JS ke backend;
Coba dulu hasil running dari kode berikut:
Copy kode kodenya sebagai berikut:
public static void main(String[] args) melempar Pengecualian {
Tali str = "tali";
// -41 -42 -73 -5 -76 -82
printArray(str.getBytes());
// -27 -83 -105 -25 -84 -90 -28 -72 -78
printArray(str.getBytes("utf-8"));
// ???
System.out.println(String baru(str.getBytes(), "utf-8"));
// Yingjuan?
System.out.println(String baru(str.getBytes("utf-8"), "gbk"));
//Karakter??
System.out.println(String baru("瀛涓?".getBytes("gbk"), "utf-8"));
// -41 -42 -73 -5 63 63
printArray(String baru("Yingjuan?".getBytes("gbk"), "utf-8").getBytes());
}
public static void printArray(byte[] bs){
for(int i = 0; i < bs.panjang; i++){
Sistem.keluar.cetak(bs[i] + " ");
}
Sistem.keluar.println();
}
Outputnya dijelaskan dalam komentar di program:
Karena 2 byte di GBK mewakili karakter Cina, maka ada 6 byte;
Karena 3 byte dalam UTF-8 mewakili karakter Cina, ada 9 byte;
Karena array byte yang tidak dapat dihasilkan oleh GBK digunakan untuk menghasilkan string sesuai dengan aturan UTF-8, ??? akan ditampilkan;
Inilah alasan mengapa karakter kacau sering dijumpai GBK menggunakan byte yang dihasilkan oleh UTF-8 untuk menghasilkan string;
Meskipun kode yang dihasilkan di atas kacau, komputer tidak berpikir demikian, sehingga masih bisa mendapatkan array byte melalui getBytes, dan UTF-8 dalam array ini dapat dikenali;
Dua 63 (?) terakhir harus diisi dengan encode (atau tidak ada cukup byte untuk diisi secara langsung, saya tidak melihat dengan cermat tempat ini);
Karena pengkodean huruf dan angka antara GBK dan UTF-8 sama, tidak akan ada karakter yang kacau dalam pemrosesan karakter tersebut. Namun, pengkodean karakter Cina mereka memang berbeda. Ini adalah asal mula banyak masalah pada kode di bawah ini:
String baru(String baru("kami".getBytes("UTF-8"), "GBK").getBytes("GBK"), "UTF-8);
Jelas sekali hasil dari kode ini adalah "kita", tapi apa pengaruhnya terhadap kita? Pertama kita perhatikan:
String baru("kami".getBytes("UTF-8"), "GBK");
Hasil dari kode ini adalah kode yang kacau, dan banyak kode yang kacau yang "kacau seperti ini". Tapi ingat: kekacauan di sini adalah untuk kita, dan untuk komputer, tidak peduli apakah itu “berantakan” atau “tidak berantakan”. Ketika kita hampir menyerah, ia masih bisa mendapatkannya dari kode yang kacau melalui “getBytes( "GBK")” Ini adalah "tulang punggung", dan kemudian kita dapat menggunakan "tulang punggung" untuk mengembalikan string aslinya.
Tampaknya kode di atas dapat menyelesaikan masalah kacau antara "GBK" dan "UTF-8", tetapi solusi ini hanya terbatas pada kasus khusus: jumlah semua karakter Cina yang berurutan adalah bilangan genap! Alasannya telah disebutkan di atas dan tidak akan terulang di sini.
Lalu bagaimana cara mengatasi masalah ini?
Solusi pertama: encodeURI Mengapa menggunakan metode ini? Alasannya sangat sederhana: GBK dan UTF-8 memiliki pengkodean %, angka, dan huruf yang sama, sehingga string setelah pengkodean dijamin 100% sama di bawah kedua pengkodean ini, dan kemudian didekode untuk mendapatkan karakter .Tusuk saja. Berdasarkan format String, kita dapat menebak bahwa efisiensi pengkodean dan decoding sangat, sangat tinggi, jadi ini juga merupakan solusi yang baik.
Solusi kedua: Format pengkodean terpadu <BR>Kami menggunakan penambangan Webx di sini. Anda hanya perlu mengatur defaultCharset="UTF-8" di webx.xml.