中文字符出现乱码的原因是因为采用了错误的解码方式,换句话说,也就是没有采用与字符编码一致的方式进行解码。
那如何将乱码字符还原呢?需要分为以下几步:
1. 将当前的乱码字符以当前解码的方式编码为二进制数组,这也是还原过程;
2. 然后以正确的方式(即原编码方式)进行解码;
下面以接收到的乱码字符“鍥介檯绫冲叞”为例,再次讲解还原过程。
1. 首先获取当前乱码字符的解码方式;
# 获取默认的字符编码,本机测试为GBK
Charset.defaultCharset()
- 将其进行重新编码,还原为二进制数组;
"鍥介檯绫冲叞".getBytes("GBK")
- 对二进制数组重新进行解码;
# 最后输出为国际米兰
new String("鍥介檯绫冲叞".getBytes("GBK"), "UTF-8")
至于为什么需要重新编码与解码?很简单,这是因为每种字符编码都采用不同的方式存储字符,如UTF-8采用3个字节存储汉字,而GBK采用2个字节存储汉字。
下面我们以一个模拟的例子来加深印象:
String str = "国际米兰";
// 用GBK进行解码,结果出现乱码
String iso2GBK = new String(str.getBytes(Charset.defaultCharset()), "GBK");
// 出现乱码
System.out.println(iso2GBK);
// 还原解码,用GBK进行编码
byte[] sourceArr = iso2GBK.getBytes("GBK");
// 再次进行解码,还原字符串
String sourceStr = new String(sourceArr, Charset.defaultCharset());
System.out.println(sourceStr);
这里需要特别注意的是,在String.getBytes()方法的调用中,如果字符集选择不当,则可能出现二进制编码丢失的现象,从而导致字符难以还原的现象,如将上面的字符编码为ISO-8859
-1,由于字节存储的差异,将导致大量的二进制码丢失,最后出现字符难以还原的现象,请看下面的编码结果。
String str = "国际米兰";
# 输出结果为12,一个汉字三个字节
System.out.println(str.getBytes("UTF-8").length);
# 输出结果为4,一个汉字只有一个字节
System.out.println(str.getBytes("ISO-8859-1").length);
基于上面的输出,我们得到如下的结论:
1. 如果是UTF-8的字符,经过GB2312、GBK、ISO-8859-1编码,则会丢失大量字节,字符无法还原,GBK到ISO-8859-1类似,即多字节到少字节编码,必然出现字节丢失现象,反之,则出现字节增多现象;
2. 如果以默认的方式编码后,再以更多字节的方式解码,如GB2312转UTF-8,则可能会出现字符减少现象(依赖于字节的含义,也可能增多),但数据多半已损坏,不可还原,但以更少字节方式解码,如果满足奇偶关系,则字符可还原,参见如下例子:
// 默认为GBK编码
String str = "国际米兰";
// 用ISO-8859-1解码
String cc = new String(str.getBytes(Charset.defaultCharset()), "ISO-8859-1");
// 再次编码与解码,将字符还原
System.out.println(new String(cc.getBytes("ISO-8859-1"), "GB2312"));
- 编码与解码并不满足可逆条件,请使用中密切注意。
总结
在对字符进行编码时,一定要注意采用相同的编码(与字符的默认编码相同),否则会出现字符的二进制数组丢失现象,从而导致字符受到损坏,再也无法还原。