乱码是怎么出现的?

据传有段时间在谷歌搜索中点击任何贴吧都会自动跳转到一个叫“锟斤拷吧”的贴吧,这就引出了这篇文章主要想解释的东西:乱码。说到乱码,互联网时代,谁还没见过几把锟斤拷呢!

一首程序员打油诗,每一行都概括了一种常见的乱码,接下来会慢慢解释:

手持两把锟斤拷, 口中疾呼烫烫烫。 脚踏千朵屯屯屯, 笑看万物锘锘锘。

作为一名网络工作者,计算机专业学生,我使用电脑的时间远远长于其他人,讲道理遇到乱码的机会也应该也很多,但是奇怪的是我自己却几乎没遇到过乱码的问题呢(狂笑)。倒是好多同学写程序的时候经常遇到乱码,更为严重的是,很多计算机系里的老师在讲到乱码的时候也是糊讲一通,根本没抓到要点,很多同学还是不懂就过来问我……

所以在这篇博客中,我想介绍一些常见的乱码,写一写各种乱码出现的原因(是的!它们不一样!),还要尝试故意产生这些乱码,以实践加强理解。这样以后就再也不用担心乱码问题了!

什么是乱码?

要解决乱码问题,首先就要回答什么是乱码,是什么导致的乱码。

什么是“码”?其实,”码“指的是编码,在计算机中,无论图像、音频、文本、数字,一切都是用编码表示的。而我们讨论的乱码,指的是文本编码错误。接下来将各种会导致乱码的情况分类告诉大家。

打开文件时出现乱码

我们知道,计算机中用于文本的编码不止一种:从用于英语的ASCII编码、中文国标码GBK,到全球统一的Unicode。这时候问题就显而易见了,如果你把Unicode表示的文本当作GBK来解读会如何?当然得不到正常的结果。反过来也是一样的。所以当我们保存一段文本时,应当指定用什么编码保存,同样当我们打开一个文本文件时,应该指定要用什么编码打开

只要储存文本本身的编码和用来读取文本的编码相同,我们就能得到正确的数据。反之,就会得到乱码。

什么?你保存和打开文件的时候没有提示你选择编码?是这样的,现在程序会根据内容猜测文件的编码……不过总有猜不准的时候,那就乱码啦。

对于非常典型的乱码”锟斤拷“,情况稍微复杂一些,本文后面将详细解释。

自己写的程序输出乱码

除了编码选择不对,还有另一种可能也会导致乱码。那就是:把本来就不是文本的东西当作了文本

这个很好验证,相信很多人都试过,对一个可执行文件(exe)右键,选择用”记事本打开“,就会看到乱码。可执行程序本身不是文本,你把它当作文本打开,所以会出现乱码。

对于程序设计者,当程序未完成时内部往往会存在很多错误。错误地把内存中不是文本的部分输出导致的乱码也属于这种情况。

看一则程序员笑话:

小明去小卖铺买饮料,对老板说,要两瓶果汁。 于是老板拿给他两瓶果汁。 小明数道:第0瓶,第1瓶,第2瓶。。。 好,他拿起第二瓶喝了。 “啊,烫烫烫烫烫烫!!!”

烫烫烫和屯屯屯产生自微软VC,这是debug模式下VC对内存的初始化操作。VC会把栈中新分配的内存初始化为0xCC,而把堆中新分配的内存初始化为0xCD。很多个0xCC连起来,而如果在中文环境(GBK)输出就会变成:0xCC 0xCC -> 烫0xCD 0xCD -> 屯 。。

在笑话中,一共有两瓶果汁,从0开始数,小明却尝试去喝编号为2的果汁,出现了越界错误,访问了错误的内存,于是就烫烫烫烫烫烫……

锟斤拷

有些人会说:好吧,你说的这些我都理解,但是这么说来乱码不应该是随机的吗!前面提到的烫烫烫和屯屯屯我也能理解,但是为什么锟斤拷这三个字出现的频率这么高呀?这三个字有什么特殊之处嘛?

您好,有的。

相对于前面提到的保存编码和打开编码不匹配,锟斤拷出现的机制更为复杂一层,它出现的条件是:一段GBK的文本,当作UTF-8打开并再次保存,然后再用GBK打开

想象一下,当你错误地用UTF-8打开了原本是GBK编码的文本,看到了很多框框和问号,这时候不听话的软件帮你自动保存了!这时候文件里存进了很多框框,原本的文字就被覆盖掉了,而这些框框在GBK里显示出来就是锟斤拷,就是这样!

如果要说的专业一点的话……。Unicode中有一个特殊的字符0xFFFD

U+FFFD � replacement character used to replace an unknown or unrepresentable character

就是这个菱形框框的问号,当读到不认识的GBK编码时,就会用U+FFFD表示,这个字符用UTF-8来编码就是:0xEF 0xBF 0xBD

最为关键和精妙的地方来了,这里有三个字节,当多次重复的时候就会变成EF BF BD EF BF BD .......。而GBK编码将两个字节编码为一个字符,所以要按两个字节两个字节读过去:EF BF/BD EF/BF BD......,就变成了锟(EFBF)斤(BDEF)拷(BFBD)。!

总结

讲到这里,常见的乱码就已经介绍完了。

  • 当使用错误的编码解读其他编码的文本时会乱码(常见的有锟斤拷、���、□□□)。
  • 当程序由于错误将内存中的数据当成文本输出时会乱码。

值得一提的是,字符集(charset)和字符编码(encoding)其实并不是同一个概念,由于这两个概念非常相似,很多情况下可以替换使用,这一点在文章中没能详细说明,有兴趣的话可以自己上网搜索学习……


JunDao

1999 Words

2020-04-19 14:29 +0800