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

我们知道在计算机里面存储的最小单位为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,所以不会显示出来,而在小圆点旁边写入了几千个这样的字符,而对应这些字符,系统需要花点时间来进行渲染,所以但你点击的时候,手机会出现卡顿。

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

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,651评论 6 501
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,468评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,931评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,218评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,234评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,198评论 1 299
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,084评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,926评论 0 274
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,341评论 1 311
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,563评论 2 333
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,731评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,430评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,036评论 3 326
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,676评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,829评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,743评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,629评论 2 354

推荐阅读更多精彩内容