乱码的产生与解决

本文主要介绍乱码产生及解决,编码方式、常见编码请参考

http://www.ruanyifeng.com/blog/2007/10/ascii_unicode_and_utf-8.html
https://www.ibm.com/developerworks/cn/java/j-lo-chinesecoding/index.html

  1. 为什么会产生乱码
  2. 如何避免乱码的产生
  3. 乱码可以复原吗

1. 为什么会产生乱码

对于这个问题需要先介绍计算机中对字符如何存取的

1.1 计算机如何存储

计算机中是通过二进制的形式对文件、字符等进行存储的。那么问题来了,如何将平时使用的文字转换成二进制呢?这里就涉及到编码知识了。具体参考文章开头链接。
这里写图片描述
1.2 乱码产生条件

如上图,进行转换时需要涉及到编码问题。如果编码与解码使用的编码不一致,很有可能会导致乱码的产生。

1.3 举个栗子

@Test
public void test3(){
    String str = "你好世界";
    try {
        String GBKEncode = new String(str.getBytes("utf-8"), "GBK");
        System.out.println("GBKEncode = " + GBKEncode); // GBKEncode = 浣犲ソ涓栫晫
    } catch (UnsupportedEncodingException e) {
        e.printStackTrace();
    }
}

上述通过utf-8进行编码,使用GBK解码。前后编解码不一致,造成乱码

2. 如何避免乱码的产生

前面已经提到乱码产生的条件。只要前后只用相同的编码进行编解码,即可避免乱码的产生。
在java中使用String操作字符串最常见的一个构造方式是

String res = new String(str.getBytes(), “encoding”);

举个栗子:

@Test
public void test4(){
    String str = "你好世界";
    try {
        String encode = new String(str.getBytes(), "GBK");
        String decode = new String(encode.getBytes(), "GBK");
        System.out.println("decode = " + decode);//decode = 娴g姴銈芥稉鏍櫕
    } catch (UnsupportedEncodingException e) {
        e.printStackTrace();
    }
}

前后使用编解码一致,为什么还会出现乱码呢?这是因为encode.getBytes()的缘故。查看jdk源码发现,当没有指定参数时,使用的是系统的编码方式,当前项目中使用的是utf-8,所以出现了乱码情况,下面稍作修改。
这里写图片描述

@Test
public void test4(){
    String str = "你好世界";
    try {
        String encode = new String(str.getBytes(), "GBK");
        String decode = new String(encode.getBytes("GBK"), "utf-8");
        System.out.println("decode = " + decode);//decode = 你好世界
    } catch (UnsupportedEncodingException e) {
        e.printStackTrace();
    }
}

3. 乱码可以复原吗

既然提出了这个问题,说明复原还是可能的,只不过要求比较苛刻。

3.1. 编解码过程中不能出现大小类型编码方式混合的情况。
什么意思:

@Test
public void run1(){
    String str = "你好hello";
    try {
        String encode1 = new String(str.getBytes("utf-8"), "GBK");
        //  黑洞出现
        String decode1 = new String(encode1.getBytes("ISO-8859-1"), "utf-8");
        String encode2 = new String(decode1.getBytes("utf-8"), "ISO-8859-1");
        String decode2 = new String(encode2.getBytes("GBK"), "utf-8");
        System.out.println("decode = " + decode2);  //decode = ???hello
    } catch (UnsupportedEncodingException e) {
        e.printStackTrace();
    }
}

如代码所示,其中大小类型编码方式混合使用,会造成丢失(黑洞现象),即小类型编码无法表示大类型编码所代表的字符,会按照该类型编码的最大值进行表示。这样就造成了对应字符不正确的情况

3.2 . 不能使用固定编码进行编码,用动态编码进行解码

固定编码:是指固定用几个字节来表示一个字符。这种情况编码中不用存储字符所需字节大小信息。如:GBK等
动态编码:指的是根据不同字符使用不同位数的字节进行表示。在字节存储中包含了多少位字节表示一个字符。如UTF-8,其编码方式如(1110xxxx 10xxxxxx 10xxxxxx),代表三个字节表示一个字符。(110xxxxx 10xxxxxx),代表两个字节表示一个字符,(xxxxxxxx)代表一个字节表示一个字符。

@Test
public void run1(){
    String str = "你好hello";
    try {
        String encode1 = new String(str.getBytes("GBK"), "UTF-8");
        String decode1 = new String(encode1.getBytes("UTF-8"), "GBK");
        System.out.println("固定 --> 变长 = " + decode1);

        String encode2 = new String(str.getBytes("UTF-8"), "GBK");
        String decode2 = new String(encode2.getBytes("GBK"), "UTF-8");
        System.out.println("变长 --> 固定 = " + decode2);

        //固定 --> 变长 = 锟斤拷锟絟ello
        //变长 --> 固定 = 你好hello
    } catch (UnsupportedEncodingException e) {
        e.printStackTrace();
    }
}

所以:如果使用固定编码进行编码。变长编码进行解码,会导致变长编码无法正常进行编码读取。反过来,则是可以的。

这是两个基本条件,但也存在特例,如:
这里写图片描述
(摘自大神的文章:传送门
这个违反了第一条,这是因为使用大类型编码后,产生的字节被小类型编码一个一个的表示再用小类型编码进行编码。其实整个过程并没有做过多的编码处理。所以结果是正确的。

总结:其实乱码的产生,就是由于编码和解码时所使用的编码方式没能对应上。统一编码,从源头解决问题!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值