#22.编码解码
未完成,误导自负 : )
##22.1 JVM中的编码
在JVM内部,字符串数据总是以unicode码的形式与JVM虚拟机交互,然而当字符串数据需要输出到系统外部时,例如输出到控制台,数据就会转为具体的编码格式进行输出。
System.out.println("中国");
- 无论文件以任何编码格式载入到JVM中,"中国"二字都会以unicode码的形式存在JVM中(\u4e2d\u56fd);
- 假如IDE控制台的编码为UTF-8,当对"中国"二字进行控制台输出时,系统会将unicode码转为具体的UTF-8编码格式。(\u4e2d\u56fd -> e4b8ade59bbd)
"d6d0b9fa" (GBK编码格式数据)
↓ 将数据已GBK格式转为Unicode码载入JVM中
"\u4e2d\u56fd" (数据的unicode码表示)
↓ 将unicode码转为UTF-8格式数据输出到控制台
"e4b8ade59bbd"
一旦数据载入JVM中,就以unicode码的形式存在JVM中,此时并没有具体的编码格式,这时,你可以将数据转为任意编码格式。
public class Test {
public static void main(String[] args) throws UnsupportedEncodingException {
/**
* 数据以unicode码的形式载入JVM中,此时你可以将其转为任意编码格式
*/
String str = "中国";
System.out.println(toHexString(str.getBytes())); // e4b8ade59bbd 系统默认UTF-8
System.out.println(toHexString(str.getBytes("GBK"))); // d6d0b9fa GB2312
System.out.println(toHexString(str.getBytes("GB2312"))); // d6d0b9fa GBK
System.out.println(toHexString(str.getBytes("UTF-8"))); // e4b8ade59bbd UTF-8
System.out.println(toHexString(str.getBytes("UTF-16"))); // feff4e2d56fd UTF-16
}
public static String toHexString(byte[] data) {
final int l = data.length;
final char[] out = new char[l << 1];
for (int i = 0, j = 0; i < l; i++) {
out[j++] = DIGITS_LOWER[(0xF0 & data[i]) >>> 4];
out[j++] = DIGITS_LOWER[(0x0F & data[i])];
}
return new String(out);
}
final static char[] DIGITS_LOWER = { '0', '1', '2', '3', '4', '5', '6',
'7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' };
}
在下面这个例子中,字符串数据以GBK编码格式载入在JVM中,然后进行输出,控制台的编码格式为UTF-8。
@Test
public void gbk_unicode_utf8() throws DecoderException,
UnsupportedEncodingException {
byte[] bytes = Hex.decodeHex("d6d0b9fa".toCharArray());
String str = new String(bytes, "GBK"); // 转成字符串数据,此时str是unicode编码
System.out.println(str); // 输出"中国"
}
在这个过程中,我们并没有将GBK格式数据转为UTF-8,而是JVM隐式地将GBK数据转为unicode码,然后再输出时根据控制台的编码再转为UTF-8编码格式。JVM相当于一个中间人,解耦了GBK与UTF-8,不必考虑原始编码与目标编码不一致而导致乱码的输出。
我们在操作IO流的时候,可以获取字节流和字符流。字节流就是IO的二进制形式,由于计算机最小的操作单元的字节,所以我们称之为字节流。 例如一个文件的二进制位01001010
(对应ASCII字符J),则读取到字节流中也为01001010
,使用十六进制表示二级制,输出的结果为4A
。
而如果使用字符流的形式读取IO流时,JVM则会使用指定编码(如果没指定,则使用系统默认编码)来对10序列进行读取,然后统一转为unicode编码,同样是"01001010"载入到JVM中后则变成了unicode序列FEFF004A
。
实际上,这里的unicode编码对应的是unicode的UTF-16
编码。以16位定长的二进制表示一个unicode码。