编码与解码

2020-04-24  本文已影响0人  寂静的春天1988

学习视频地址http://yun.itheima.com/course/638.html

1、什么是编码与解码
在计算机中存储着都是二进制的字符(0,1),自然语言的字符通过编码表来沟通计算机和人类之间的语言。比如字母A在ASCII编码表中是65,又对应着二进制0110 0001。这样程序输入A,通过编码表A对应着的65找到二进制0110 0001再解释给计算机。反之也是一样。
2、ASCII
ASCII编码是计算机发明之初发明的编码表,只考虑了美国的需求,大概只需要128个字符。128个字符刚好7个字符就可以表示,ASCII中则最高位为0,剩下7位表示字符,这7位可以看作数字0-127,ASCII编码中规定了0-127分别代表什么含义。


image.png
char a=33;
System.out.println(a);//!

3、ISO-8859-1编码
ISO-8859-1编码用来表示西欧的一些字符(ISO-8859-1收录除了ASCII收录的字符外,还包括西欧语言、希腊语、泰语、阿拉伯语、希伯来语对应的文字符号),其中0-127 与ASCII编码表示的一样(兼容ASCII)128-255规定了不同的涵义。

4、windows-1252
windows-1252也是西欧的一套编码,虽然ISO-8859-1编码号称是标准,但是由于出来的较早,连欧元€这个字符都没有,所以更广泛用的实际上是windows-1252编码,windows-1252和ISO-8859-1基本一致,区别在于ISO-8859-1中128-159都用来表示控制字符,而windows-1252中使用了一些数字表示可打印字符,windows-1252加入了欧元€字符和一些其他常用字符,基本上可以看作ISO-8859-1已经被windows-1252所代替。一些文件的声明即使是ISO-8859-1编码,解析的时候仍然应该使用windows-1252解析。

5、GB2312
中文的第一个标准是GB2312,针对的主要是常见的中文简体字,不包括罕见字和繁体字。GB2312固定使用2个字节表示字符,两个字节中最高位都是1,如果是0就认为ASCII字符

6、GBK
GBK建立在GB2312的基础上,向下兼容GB2312,新增了一万四千多字,包括繁体字

7、GB18030
GB18030向下兼容GBK,增加了五万五千多个字符,包含了很多少数民族字符,还包括中日韩统一字符,

8、兼容和乱码

String b=new String("恒".getBytes("GB2312"),"GBK");
System.out.println(b);

GBK是兼容GB2312的,所以即使用GB2312编码,然后用GBK解码。也不会乱码。

String b=new String("橫".getBytes("GB2312"),"GB2312");
System.out.println(b);

GB2312中没有繁体字的,所以繁体字即使用GB2312进行编码,然后用GB2312解码也会乱码。因为使用"橫".getBytes("GB2312")进行编码的时候根本就找不到对应的编码号。解码的时候自然就会乱码了。

String b=new String("橫".getBytes("GBK"),"GB2312");
System.out.println(b);

这样也会乱码,GBK中虽然有繁体字的编码,但是GB2312解码时却是找不到这个编码号的(因为GB2312中不存在繁体字),自然这样虽然可以使用GBK编码,但是使用GB2312解码时也会乱码。

9、Unicode
世界上存在着太多国家的字符,每个国家的计算机厂商都只对自己的国家常用的字符进行了编码,这样就出现了太多的编码,且互相不兼容。Unicode统一了世界上所有的字符进行编码。Unicode给世界上所有字符都给了一个数字编码,但是Unicode没有规定这个数字编码对应着的二进制编号。而需要将这个Unicode分配的数字编码转换成计算机能够存储的二进制码,需要通过UTF-8/UTF-16/UTF-32进行转换。

10、UTF-32
UTF-32就是字符编号的整数二进制形式,四个字节。但每个字符都使用四个字节非常的浪费空间,所有很少采用。

11、UTF-16
Unicode编码相当于一本很厚的字典,这么多字符不是一次性定义的,分区定义,每个区可以存放65536个字符,称为一个平面,目前总共有17个平面。
最前面的65536个字符位,称为基本平面,所有最常见的字符都放在这个平面当中,16进制的范围就是U+0000-U+FFFF(0-65535)。剩下的字符都放在辅助平面。码点范围从U+010000到U+10FFFF。
UTF-16介于UTF-32和UTF-8之间,结合了定长和变长的特点。编码规则是:基本平面的字符占用2个字节(U+0000到U+FFFF),辅助平面的字符占用四个字节(U+010000到U+10FFFF,额用计算器看了一下10FFFF换算成二进制是3个字节啊。为什么资料上都说是4个字节?)。
为了将utf-16d两个字节的编码和四个字节的编码区分出来,Unicode的设计者将0xD800-0xDFFF(55296-57343)保留下来,并称为代理区。
复制平面的字符一共有2^20个字符。因此表示 辅助字符至少需要20个二进制位。Unicode将这个20个二进制位分成两半,前10位映射到U+D800到U+DBFF(55296-56319),称为高代理位,后10位映射到U+DC00-U+DFFF(56320-57343)称为低代理位,这意味着,一个辅助平面的字符被拆分成两个基本平面字符表示。
如果Unicode码>=10000(65536),则说明他是辅助平面的字符,险将u=u-0x10000,然后将u写成二进制形式:yyyy yyyy yyxx xxxx xxxx,u的utf-16编码(二进制就是)110110yyyyyyyyyy 110111xxxxxxxxxx。这里将二进制分成了2份,第一份时高代理位,第二份时低代理位。
为什么高代理位的前6位要补110110 ,低代理位的前6位要补110111呢?
因为高代理位:110110yyyyyyyyyy 最大最小的范围为1101100000000000-1101101111111111,即对应着16进制0xD800-0xDBFF,后两个字节的取值范围(二进制)是1101110000000000-1101111111111111即对应着16进制是0xC00-0xDFFF。(这样也解释了我前面一个疑惑,为什么前面说UTF-16辅助字符是四个字节,因为高代理位,和低代理位补了数字 这样 12+20 也就是4个字节的位数!)

12、UTF-8

utf-8使用变长字节表示,每个字符使用的字节个数与其Unicode编号大小有关,编号小的使用字节就少,编号大的使用字节就多。使用个数在一到4个不等。 image.png

英文和数字都使用一个字节,并且兼容ASCII码,而常见的中文汉字一般都在第三个,也就是使用3个字符表示。
utf-8兼容ASCII码,而utf-16和utf-32是不兼容ASCII码的。

13、getBytes方法
str.getBytes()方法可以指定通过什么编码方式进行编码str.getBytes("GBK"),如果不在形参中指定编码格式是通过jvm默认的编码方式进行编码,可以通过System.getProperty("file.encoding")来获取到系统默认编码格式!

14、new String()方法。
new String()方法和getBytes方法一样,也可以在形参中指定解码格式new String(bytes,"GBK");如果不指定也是file.encoding属性的值。

15、ISO-8859-1的妙用

        String s="黑马";
        byte[] bytes=s.getBytes("GBK");
        System.out.println(Arrays.toString(bytes));//[-70, -38, -62, -19]
        String str=new String(bytes,"UTF-8");
        System.out.println("str="+str);//str=����
        //注意由于是使用GBK编码的,但是使用UTF-8解码时,找不到对应的字符,就解析成了四个问号,此时黑马对应的编码已经变了,变成了四个问号的编码
        byte[] bytes1=str.getBytes("UTF-8");
        System.out.println(Arrays.toString(bytes1));//[-17, -65, -67, -17, -65, -67, -17, -65, -67, -17, -65, -67]
        //这时想通过UTF-8再找回字符编码是找不到的,找的是四个问号的字符编码,
        String str1=new String(bytes1,"GBK");
        System.out.println("str1="+str1);//str1=锟斤拷锟斤拷

妙用ISO-8859-1

        String s="黑马";
        byte[] bytes=s.getBytes("GBK");
        System.out.println(Arrays.toString(bytes));//[-70, -38, -62, -19]
        String str=new String(bytes,"ISO-8859-1");
        System.out.println("str="+str);
        byte[] bytes1=str.getBytes("ISO-8859-1");
        System.out.println(Arrays.toString(bytes1));
        String str1=new String(bytes1,"GBK");
        System.out.println("str1="+str1);//str1=黑马

因为ISO-8859-1每一个字节都对应着一个字符编码(所以不存在找不到相应的字符,从而给一个问号之类的字符编码),不会去改变字节的编码!所以再str.getBytes("ISO-8859-1")时,还是可以找到相应的byte数组,此时再通过正确的解码方式解码,也不会造成乱码!

16、字符输入流原理

    public static void main(String[] args) throws IOException {
        InputStreamReader isr=new InputStreamReader(new FileInputStream("a.txt"),"UTF-8");
        int ch=1;
        while((ch=isr.read())!=-1) {
            System.out.print((char)ch);
        }
        isr.close();
    }

a.txt中实际存储着的是UTF-8的字节(我的eclipse默认文件编码是UTF-8),你能看到字符实际上是eclipse解码后的结果。
new FileInputStream("a.txt")将a.txt中的字节读取出来,new InputStreamReader(new FileInputStream("a.txt"),"UTF-8"),将a.txt的字节根据UTF-8方式进行解码。

17、获取系统默认编码
当不指定编码,那么使用的是什么编码呢?
log.info("系统默认编码====》"+System.getProperty("file.encoding"));可以使用这行获得,系统默认编码。

上一篇 下一篇

猜你喜欢

热点阅读