Respose响应时浏览器中文乱码的根本原因

感谢@giddyer 大大的分享,受益良多。
原文地址:https://blog.csdn.net/java_gchsh/article/details/74173498
另可参考默认解码方式:https://blog.csdn.net/laozhang1024/article/details/88059612

先说一下什么叫乱码

不知道有没有人这样认为过,一个字符串不仅仅包含字符,还有隐藏着它的编码信息。比如java中String str = "你好";我之前是这样认为的,str这个字符串隐藏着它的编码方式unicode编码或者gbk、iso-8859-1等。这种理解是错误的,字符就是字符没有任何其他信息,正确的理解应该是,人在一个文件中所看到的字符串是系统经过把内存中的数码信息读取也再解码成一些字符最后显示,就是当你双击打开一个文本文件时系统会把内存的数码信息读取显示出来,当你保存一个文本文件时系统会把这个文件以你所设置的编码方式编码,再放进内存中。


所以说乱码也是一些字符,只是奇怪的字符而已,并没有什么”码“。

接着说乱码产生的原因

我们经常看到网上这样解释乱码原因:乱码是因为解码方式和编码方式不一致导致的,这句话本身没有错,但同样这句话的本身就是把乱码概括了而已,它并不能帮助你理解乱码。

所以我们要提的问题是:为什么解码方式和编码方式不一致会出现乱码。

这里以utf-8,gbk,iso-8859-1三种编码方式为例。 @Test


  
  
  1. @Test
  2. public void testEncode() throws Exception {
  3. String str = "你好",en = "h?h";
  4. System.out.println( "========中文字符utf-8=======");
  5. byte[] utf8 = str.getBytes(); // 以utf-8方式编码 ,default:utf-8
  6. for ( byte b : utf8) {
  7. System.out.print(b + "\t");
  8. }
  9. System.out.println( "\n"+ "========英文字符utf-8=======");
  10. byte[] utf8_en = en.getBytes(); // 以utf-8方式编码 ,default:utf-8
  11. for ( byte b : utf8_en) {
  12. System.out.print(b + "\t");
  13. }
  14. System.out.println( "\n"+ "========中文字符gbk=========");
  15. byte[] gbk = str.getBytes( "gbk");
  16. for ( byte b : gbk) {
  17. System.out.print(b + "\t");
  18. }
  19. System.out.println( "\n"+ "========英文字符gbk=========");
  20. byte[] gbk_en = en.getBytes( "gbk");
  21. for ( byte b : gbk_en) {
  22. System.out.print(b + "\t");
  23. }
  24. String s = new String(utf8, "utf-8");
  25. String s1 = new String(utf8, "gbk");
  26. System.out.println( "\n"+s + "====gbk:" + s1);
  27. }


扫描二维码关注公众号,回复: 5375620 查看本文章

测试上面方法,打印的结果是

========中文字符utf-8=======
-28 -67 -96 -27 -91 -67
========英文字符utf-8=======
104 63 104
========中文字符gbk=========
-60 -29 -70 -61
========英文字符gbk=========
104 63 104
你好====gbk:浣犲ソ

------------------------------------------------------------------------------------

可以得出结论:

一个中文字符以utf-8编码会转成3个byte,如果以gbk编码会转成2个byte;

一个英文字符以utf-8编码会转成1个byte,如果以gbk编码会转成1个byte。

从打印的最后一行结合29-31行代码可以看出,如果把byte数组utf8 以utf-8的方式解码不会有乱码,还是原来的”你好“,而如果以gbk方式解码则出现了三个乱码字符,为什么是3个而不是2个呢,6/2=3。

接下来说iso-8859-1,这种编码应用于英文系列,也就是说不能表示中文(如果要使用必须依赖于其它兼容iso-8859-1编码方式的编码),它读不懂的字符都将被视为英文问号'?',英文问号的iso-8859-1编码号是:63(十进制)(其实在几乎所有的编码方式中,所有英文字符都用1个固定的字节码表示,unicode编码除外)。


  
  
  1. @Test
  2. public void testISO() throws Exception {
  3. String str = "你好";
  4. byte[] bs = str.getBytes( "iso-8859-1");
  5. for ( byte b : bs) {
  6. System.out.println(b);
  7. }
  8. System.out.println( new String(bs, "iso-8859-1"));
  9. System.out.println( new String(bs, "utf-8"));
  10. System.out.println( new String(bs, "gbk"));
  11. System.out.println( new String(bs, "unicode"));
  12. }
打印结果

63

63
??
??
??
㼿

说明63 =》?,所有中文都被认为是?,所以说当执行这句代码时:byte[] bs = "你好".getBytes("iso-8859-1");信息已丢失。

再执行String str = new String(bs,"任何charset");str已经不等于"你好"了,而是两个问号??。所以在tomcat中我们会经常遇上中文变为一长串??????,就是源于此。

在iso-8859-1、utf-8、gbk中一个字节码表示一个英文字符,

在unicode编码中一个字节码并不能表示任何字符,而且规定必须是两个字节码(有时4个)才能表示一个字符。


说了这么多,也许很多人会问为什么要用这么多编码方式,统一成utf-8不就能表示所有字符了?

编码不仅仅是要考虑是否能表示任何字符,还要考虑传输和存储。

1、utf-8确实几乎能表示所有已知字符。前面说过在utf-8编码中3个字节才表示一个中文字符,这样显然占空间,不利于传输和存储(传输和存储都是以二进制的方式进行的)

2、无疑一个字节表示一个字符最省空间,比如iso-8859-1。但这世上不是只有英文字符,还有各个地区国家的文字。所以字符的数量肯定是大于2的8次方的。

所以结合以上两点,就自然地出现了很多种编码方式。


了解各种编码方式的规则:https://jingyan.baidu.com/article/020278118741e91bcd9ce566.html

猜你喜欢

转载自blog.csdn.net/laozhang1024/article/details/88050133
今日推荐