美文网首页
乱码问题(一)编码那些事

乱码问题(一)编码那些事

作者: 风轻云淡_z | 来源:发表于2019-08-20 00:33 被阅读0次

    我们知道在计算机里面存储的最小单位为bit,而8个bit构成了一个字节(byte)。字节是我们能用编程语言控制读写的最小单位。如果我们想一个bit一个bit写的话只能去对每个比特位进行缓存然后左移相加,当凑齐8个时再按照一个字节进行写入。

    字符和字节是什么关系

    字符,也会是我们常见的文字符号,比如A、B、C、1、2、3、你、好、啊,这些字符构成了我们的文字,而他们的组合又构成了我们的语言。当然如果狭义的来说,字符还包括很多看不见的转义字符。
    字节是计算机存储的单位,而字符是人的单位。人的单位和计算机的单位又不是完全等同的,当我们想把字符在计算机上表示时,就必须要做一些转换,而这个转换规则,就是我们常说的编码。而我们常见的乱码原因其实都是源于写入编码和读取编码不一致。
    在HTML的meta标签里有个charset属性,其作用就是设置读取的编码,如果不设置,则会按照操作系统默认的编码进行读取,在windows下为GBK,Linux下为UTF-8。下面一个最基本的网页,我们以UTF-8编码进行保存

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Title</title>
    </head>
    <body>
    这是一段中文文字
    </body>
    </html>
    
    在浏览器打开没问题,

    当我们把

     <meta charset="UTF-8">
    

    这一行删掉再打开时,由于没设置编码,故浏览器会采用默认的GBK编码进行读取,所以就会出现下面的乱码。


    编码的历史

    最早的ASCII码

    你可能经常听到什么GBK、UTF-8、ASCII、ISO-8859-1......这些乱七八糟的编码名词,心中可能会有疑问为什么会有这么多的编码,只存在一个不好吗,这样也就不存在那些恶心的乱码问题了。其实,这些东西表面上看起来很乱,如果我们从历史的角度上缕一缕,就会明白为什么会有这么多的编码了。
    我们知道,计算机早期基本上都是美国人在研究和使用。为了表示方便,他们就想能否用一系列数字来代替那些字符。于是就出来ASCII就出来了,在ASCII码里,0-32有不表示字符,他们有自己特殊的含义,比如7代表响铃、10代表换行。从33到126每一个分别代表一个特定的字符,比如57代表阿拉伯数字9、85代表英文字母U。这样当计算机遇到这些数字时就会显示对应代表的字符。
    ASCII码采用一个字节来编码一个字符,也就是计算机每读到一个字节,就会去ASCII码表里查找它对应的字节,然后把它给显示出来。

    各种扩展ASCII编码百花齐放时代

    随着世界的发展,计算机也逐渐传到各个国家,这时就存在一个问题了,如何在计算机上表示本国的文字。毕竟计算机毕竟是人家美国发明的,他们刚开始设计的时候也没有考虑太多,所以其他国家的文字如何在计算机上显示就成了一个问题了。就以中文为例,ascii码是采用一个字节即八位来表示一个字符的,所以理论上最多能存2^8即256种字符,就算这样,也没法完全表示所有的中文字符啊,就拿比较常用的《新华字典》来说,其里面也有10000多个汉字。而且如果我们要重写ascii码的话,那么在中文机器上又无法正确显示英文字符了。所以现在面临着两个问题

    1. 如何表示这些中文字符
    2. 在表示这些字符的时候如何做到与ascii码兼容

    但这丝毫难不倒我们聪明的中国人民,既然一个字节表示不了,那我两个总可以了吧,我们知道两个字节也就是16位,最多能表示2^16即65536个字符,对付常见的中文字符是足够了。但是另一个问题该怎么解决呢,这时,我们聪明的中国人民又想到了,既然前127位被你用了,那我就从127后面开始编码不就行了,这样当读取的字节值小于127时,我们就把它当成ASCII码来处理,当读取到的值大于127时,我们就再把它后面一位字节读取下,然后把这两个字节翻译成对应的汉字。然后最早的中文编码GB2312就出来了,后来微软对GB2312进行扩展,同时向下兼容GB2312,制定了GBK编码,最早出现于Windows 95简体中文版中,这也是我们现在windows计算机中文默认使用的编码。后来国家又发布了GB 18030编码标准,对GB2312进行了一些补充。在这里需要补充的是GBK是微软的标准并不是国家标准。

    下面我们用代码演示具体编码

    下面一段代码用来获取字符串s1用GB2312编码后的字符数组并输出

        @Test
        public void testGbk() throws UnsupportedEncodingException
        {
            String s1 = "中国ABC";
            byte[] bytes1 = s1.getBytes("GB2312");
            for(byte b: bytes1)
            {
                System.out.print(b + " ");
            }
        }
    

    得到下面输出结果


    这里由于byte在java里默认是有符号类型的,所以最高位的1被当成符号位了输出了负数。具体解释下就是ascii码在0-127之间,所以对应的8位比特最高为永远为0,最大的127对应的8位比特为0111 1111,而GB2312是从128开始的,所以对应的第一个bit位位一,而在计算机中,第一位是当作符号位处理的,中文编码是从128开始的,这时第一位永远是1,所以会输出负值。( 具体怎么转换请搜索原码、反码、补码关键词)
    我们对输出结果进行下改进,以便更直观的看到编码结果
        @Test
        public void testGbk() throws UnsupportedEncodingException
        {
            String s1 = "中国ABC";
            byte[] bytes1 = s1.getBytes("GB2312");
            for(byte b: bytes1)
            {
                System.out.print(Integer.toHexString(b & 0xff) + " ");
            }
        }
    

    这里我们把它强转成int并通过& oxff屏蔽掉变成int后多出来的位,最后以16进制输出。结果如下


    通过刚才以及现在的输出我们可以推断d6 d0的GB2312码,b9 fa的GB2312码,而41 42 43刚好对应ASCII码的41 42 43。所以在GB2312码里中文字符占两个字节,英文字符占一个字节
    我们把编码换成GBK,得到下面输出

    跟刚才的一样。GBK是兼容GB2312的,可以把GB2312看成GBK的子集。

    就这样,各个国家也都开始像中国这样通过扩展ASCII码来制定自己的编码,于是出现了一堆编码,比如繁体字的BIG5,日文的Shift_JIS,韩文的EUC_KR......但这些编码面临着一个很大的问题,它们之间互相都不兼容。

    统一标准的Unicode

    刚才提到了各个国家都通通的去制定自己的文字编码,但是这些文字编码又互相不兼容,所以当一份文字通过互联网在不同国家传递时很容易出现乱码问题。
    后来出现了一个叫国际标准化组织ISO和Unicode的协会,他们像设计一个字符集,可以把全世界的文字都包含进去,以图统一编码。而这个字符集就是Unicode字符集。注意Unicode是一个字符集,不是某一个编码。我们知道中文是不包括拉丁文的,同样拉丁文也不包括俄罗斯文,所以Unicode就是一个即包含了中文,又包含了拉丁文、日文、俄文......的字符集,我们常用的emoji表情也是Unicode里面的。总之你可以在里面找到任何一个国家的字符。
    而在Unicode里每一个字符都有对应的Unicode码,比如U+4E2D代表U+56fd代表 。但是这只代表了符号的二进制码,而将它存入计算机依然需要进行编码。而Unicode对应的编码规则有很多,比如我们常见的UTF-8,java里使用的 UTF-16BE编码。而不同Unicode编码会有很大差别。

        @Test
        public void testUTF8() throws UnsupportedEncodingException
        {
            String s1 = "中国ABC";
            byte[] bytes1 = s1.getBytes("UTF-8");
            for(byte b: bytes1)
            {
                System.out.print(Integer.toHexString(b & 0xff) + " ");
            }
        }
    

    得到如下结果


    image.png

    我们可以看到UTF-8编码中中文占三个字节,英文占1个字节

    如果采用UTF-16BE,会得到下面结果


    在UTF-16BE编码中,不管是中文还是英文,都统统占用两个字节
    可以发现UTF-8兼容ASCII码,所以用的比较多。

    在unicode里有许多特殊转义字符,利用它我们可以做出许多特殊的效果。比如之前传的比较火的千万不要动到黑点,手机会卡死机 <> ‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎ ‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎ ‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎ ‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎ ‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎ ‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎ ‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎ ‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎ ‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏ ‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏⁧erehhcuot
    这句话,其实就是Unicode特殊转义字符。在Unicode里有两个字符表示书写方向,这两个字符宽度位0,所以不会显示出来,而在小圆点旁边写入了几千个这样的字符,而对应这些字符,系统需要花点时间来进行渲染,所以但你点击的时候,手机会出现卡顿。

    乱码问题(二)常见乱码情况分析与解决方法

    相关文章

      网友评论

          本文标题:乱码问题(一)编码那些事

          本文链接:https://www.haomeiwen.com/subject/jvhysctx.html