作为开发人员,或许听说过“奇怪的联通现象”。没有听说也没有关系,那什么是“联通现象”?到底是什么原因造成这个现象产生呢?本文帮你解疑答惑。
有个很著名的奇怪现象:当你在 windows系统 的记事本里新建一个空白文件,在文件里输入"联通"两个字之后保存,关闭文件后再双击打开。观察到什么了吗?输入“力挺联通”,保存后再双击打开,又会如何呢?
输入“联通”两个字的时候:
保存并关闭文件,双击打开后的结果:
输入“力挺联通”四个字的时候:
保存并关闭文件,双击打开后的结果:
你会发现,明明输入的是正确的文字,但是关闭后再双击打开文件,发现文字消失,取而代之的是几个乱码!为什么会这样呢?大家知道,记事本是微软的产品。莫非联通得罪了微软?
为了能透彻理解这个奇怪的现象,一定要透彻理解字符编码,尤其是UTF-8编码格式。
接下来让我们来分析一下计算机对UTF-8文件的解码过程。掌握了UTF-8文件的解码过程,那么就具有了解释“奇怪的联通现象”的技术基础了。
好吧!说干就干。
有一个UTF-8编码的文本,文本内容为: “a0一” 。分别是英文字母“a”,数字“0”,中文汉字“一”。接下来我们来分析一下这个文件,计算机是如何识别的吧。
一、获取十六进制编码的内容
在这里介绍一个软件:UltraEdit文本编辑器。用这个软件,可以查看文本的十六进制代码。
1、在文本编辑区输入内容
2、选择字符编码格式
3、点击“16进制编辑”按钮,查看文本内容的16进制
所以,获取到“a0一”的16进制为:
61 30 E4 B8 80
二、将16进制的内容转成二进制
进制转化可以通过在线进制转换工具实现。https://tool.lu/hexconvert/
1、输入网址
2、选择进制
3、在文本框输入内容
4、点击“转换”按钮
5、查看对应进制的内容
所以16进制:61 30 E4 B8 80,转成二进制后如下:(不足8位长度的在数字前端补0)
01100001
00110000
11100100
10111000
10000000
每8个数字代表一个字节,所以能看出该文本共有五个字节。但是到底哪几个字节是一个字符单元呢?计算机是如何分组的呢?
因为该文件保存的格式是UTF-8编码格式,我们来回顾一下UTF-8编码的特点:
1、UTF-8编码是可变字节编码。所以每8个字节并不一定就是一个字符。有可能8个字节是一个字符,有可能16字节是一个字符,有可能24字节是一个字符。
2、文本读取是一个字节一个字节的来读取,根据字节开头的标志位来识别,从而能确定到底几个字节是一个字符单元。
3、UTF-8编码规则中,原Unicode前128个字符是单字节编码(实体编号在127以内),编号在128至2047的是双字节编码(2的11次方=2048),编号在2048之后就是三字节编码。
(1)、如果字节的第一位是0,则说明这个字节是单字节;
(2)、如果第一个字节的前3位是110,第2个字节的前2位是10,符合这个规律的连续相连的两个字节就代表一个双字节的字符;
(3)、如果第1个字节的前4位是1110,第2个字节的前2位是10,第3个字节的前2位是10,符合这个规律的连续相连的三个字节就代表一个三字节的字符。
三、根据UTF-8编码规则,以上二进制内容被分为三个组:
01100001 第一个字符
00110000 第二个字符
11100100 以下三个字节是一个中文字符,符合1110xxxx 10xxxxxx 10xxxxxx的格式
10111000
10000000
四、重新计算,得出对应Unicode字符集的二进制编码
Unicode字符集是双字节编码,所以每16位数字代表一个字符。
删除标记位上的数字,将剩余的二进制数字合在一起,不足16位的,在数字前补足0。根据这个规则,以上二进制数字就变成了以下三组二进制的数字。
00000000 01100001 第一个字符
00000000 00110000 第二个字符
01001110 00000000 第三个字符
五、将上述三组二进制数字转成10进制
进制转化可以通过在线进制转换工具实现。https://tool.lu/hexconvert/
(二进制转10进制时,要将0或1之间的空格去除,再通过工具进行转换。)
上述二进制对应的十进制数字为:
97
48
19968
六、通过10进制反查在Unicode字符集中它们对应的字符
97、48、19968其实就是Unicode字符集中字符的索引下标。Unicode字符集中索引为97的符号是哪个呢?其实熟悉ASCII表的同学都知道,是小写字母“a”。不熟悉的话也没有关系,我们可以通过以下方式来查看。
新建一个html文件,在文件中输入:
a0一
&#实体编号;——这种格式是html的实体编码格式。写好后保存关闭文件,然后双击打开,我们可以在浏览器中看到显示内容:a0一
掌握了计算机对UTF-8文件的解码过程,接下来我们来解释奇怪的“联通”乱码问题吧。
当新建一个文本文件时,记事本的编码默认是ANSI,在ANSI编码格式的文件里输入汉字,那么实际就是GB2312编码格式。在这种编码下,"联通"的十六进制编码是:
C1 AA CD A8
将C1 AA CD A8转成二进制后:
11000001
10101010
11001101
10101000
大家有没有发现,这个GB2312文件的二进制数字格式竟然与UTF-8的二进制格式巧合地撞车了。
虽然保存的是GB2312编码,但是“联通”这两个字的二进制数字正好和UTF-8的格式完全吻合,所以记事本就把这个文件当做UTF-8编码格式了,自然就以UTF-8编码方式来打开并解码了。根据UTF-8编码规则,以上二进制内容被分为两个组,内容为:
0000000001101010
0000001101101000
转为10进制后为:106和872,Unicode字符集中第一个中文字“一”,序列号是19968,所以106和872这明显不是中文。
奇怪的联通现象,出现乱码的主要原因是:GB2312编码与UTF-8编码产生了编码冲撞,导致编码误解,从而触发了错误的文件打开方式所引起。
如果输入中文"爱联通",保存文件后关闭,当再次打开,则不会出现乱码问题。因为中文“爱”在编码表中对应的二进制数据不符合UTF-8的格式,所以记事本不会误解该文件是UTF-8编码格式,就会用默认的GB2312编码来解码,自然就不会出现乱码。其实在“联通”两个字前或后多输入几个字,就不会出现这样的巧合了。
那么输入“力挺联通”四个字是不是就不会这么巧合而乱码了呢?
非常奇妙的是输入“力挺联通”,还会出现乱码。原因是“力挺”这两个字的GB2312二进制编码也恰好与UTF-8吻合。整个文件里的四个字都是符合UTF-8编码格式的,自然就会被误解为UTF-8编码的文件,那么双击打开时就会采用UTF-8编码方式来解码。巧吧!
C1 A6 CD A6
转成二进制后:
11000001
10100110
11001101
10100110
这也符合UTF-8编码的格式。
总之,只要GB2312文本的二进制编码不与UTF-8格式完全吻合,就不会被误解是UTF-8文件,也就不会因为误解而采用错误的编码方式来解码了。
其实换一种文本编辑器来打开,乱码问题就可以避免。如果没有专业的文本编辑器,甚至使用浏览器来打开,也能正常显示出中文内容。本文采用EditPlus来打开该文件。
当采用正确的编码格式来打开,原本被认为是乱码的文件就正常显示了。