在人类创造的各种信息当中,文字信息一直被我们认为是知识和智慧的重要载体,古代无数僧侣和学者,终身都献身于书籍文字的保存和传达。很多书籍靠着一代代人手工抄写而流传下来。然而这种抄写费时费力不算,还常常会导致抄写错误,导致信息的“失真”。也有很多藏书因为保管不善而毁于腐朽或者战火。
在所有需要表达的信息当中,文字是最早被纳入“数字化”的信息种类之一。摩斯电码(Morse Code)是人们发明出来用电信号表达文字的最早方法之一,也是最早的数字化通信形式。通过断的电流,产生一种一系列按顺序排列的“代码”,从而可以用来表达文字。它的代码是:点(.)、划(-)、空(每个字符间的短停顿)、中等时间的空(每个词之间的中等停顿)、长空(每个句子之间的长停顿),如图 1-8 所示。有两种“符号”用来表示字符:点(.)和划(-),或叫“滴”(Dit)和“答”(Dah)。点的长度决定了发报的速度,并且被当作发报时间参考。这种方法除了可以用在电报上,也可以用于闪光等多种媒介上。
现代计算机处理文字的方法和摩斯电码有一定的相似,只是用来代表文字的代码不再是点和划,而是真正的数字。一旦文字被计算机数字化之后,我们就拥有了一种永久性保存文字信息和快速、百分百准确的文字复制手段。当我们第一次知道:整个图书馆的书,被数字化后,只需要薄薄的几张光盘——这种载体体积的巨大差异,让我们倍感计算机的神奇。在现代几乎所有的高级编程语言中,字符或者字符串,都作为基本的数据类型存在。
计算机界使用的最广泛的,用数字来表示文字的方案,就是如图 1-9 这个方案。你可以发现这个方案使用了 0~127 这 128 个数字,来代表 128 个字符,其中最常见的就是 26 个英文字母的大小写,以及 10 个阿拉伯数字,当然还有一批符号。这些符号中有些可以用键盘直接输入,有些则不能。这个表格,或者方案的名字,就叫做《美国信息交换标准代码》,简称 ASCII。这个方案定义了我们在计算机世界中,最常见的英文字母和符号用哪些数字来代表。
当我们从计算机里得到一串数字的时候,比如从文件里读取到,或者从网络上接受到的。我们完全可以按这个编码表来解读,比如我们可能会获得 “72 101 108 108 111” 这 5 个数字的时候,按表查询,就应该是 Hello 这 5 个字母。——这个看起来是不是有点像一组密码的解码过程?实际上,如果你在电脑里建立一个文本文件,输入 Hello 这5个字母然后保存,这个文件的内容就是 72 101 108 108 111 这 5 个数字。这和所有的别的文件一样,都只是一般的数字而已。你可以尝试把文件名改成 a.exe 或者别的什么名字,都不会改变这 5 个数字的内容。你也可以尝试用你的文本编辑器,在 windows 下是 notepad,Linux 下用 cat 命令,打开任何文件,只要文件里面存放的数字是符合上面这个表格的,都会被显示成对应的文字字符。可以说这个文本编辑器本身的功能,就是把数字转换成文字来显示。
这是一篇英文文章(马丁·路德·金《我有一个梦》节选)的 txt 格式内容,如上图 1-10 所描述,实际上是一系列的数字组成,需要注意的是,这里的数字是 16 进制格式显示的数字。在文章中,因为存在很多空格,所以我们很容易分辨出 16 进制的数字 “20”,也就是十进制的数字 32,这个数字正式 ASCII 所规定的数字。
有时候我会想,为什么 ASCII 中要把文字的 “1” 定义成 49 这个奇怪的数字呢?明明数字 1 代表文字的 1 不是很自然吗?这个原因我不得而知,但是我知道,如果我们使用不同的编码表来处理同一份数据,结果一定是非常混乱的。比如你用 1 来表达文字 ‘1’,我用 49 来表达 ‘1’,最后当我收到一个文件,内容是 1 这个数字的时候,我可能会显示出不是 ‘1’ 这个文字的内容。这就跟两种不同语言的人在对话一样。因此文字编码表本身,就是计算机界用来表达文字的语言规范。所以其实具体哪个数字表示哪个字符并不重要,重要的是要统一使用一份相同的编码表。
ASCII 编码的顺序规则却是有明显意义的,你会发现 0-9 的编码是从小到大的,而字母都比数字要大,小写字母比大写字母大,这个编码方式的结果就是,如果你要按我们常见的习惯来对单词排序的话,直接使用这个编码表中的顺序,就可以直接排序了。
现在我们中文的编码表,是按照汉语拼音的排序顺序,来定义各个汉字的编码数字的,所以我们要按拼音排序,就变得异常简单,直接按他们的这些数据的大小排序就可以了!——这真是一件伟大的工作,我每次在使用汉字排序的程序的时候,都会从心底向制定汉字编码的中国科学家发出由衷的致敬。因为汉字有很多多音字,并且数量庞大,如果他的编码不是预先做好了这个排序,要程序员自己去实现这个功能,将是多么大的一个挑战!
乱码是我们在使用计算机处理文字的时候,最常碰到的问题之一,也是让我们觉得很困惑的事情。为什么我们在技术这么发达的时代,还会在文字这种看似简单的问题上碰到障碍呢?实际乱码的问题揭示了数字技术的一些本质,了解乱码产生的原因,能帮我们理解数字技术的核心概念。乱码的产生有三个原因:
在数据的基本格式——长度分割上就是用了错误的编码规则。我们知道,英文字符一般都是用一个小于 255 的数字来表示,而中文字符因为远多于 255,所以一般都要用 2 个字符,也就是在 0 到 65535。既然这些数字一个个排列起来,就有可能因为错误的方式被解读——比如前一个汉字的后半部分和后一个汉字的前半部分被看成了一个字。如果你打开一个包含中文的文本文件,然后删除掉第一个中文字的第一个字节,你会发现整个文件的文字都不能正常显示了。这揭示了一个计算机处理文字的原理——所有的那些代表文字的数字,都是被无区别的,按顺序的一个个字节读取然后处理。顺序,是数字化信息中最重要的关键。
——在“黄”字删除掉一个字节的数据后,后面的所有文字都解释错误了,直到一个单字节的数字“06”之后,文字解释恢复了正常
即使使用了相同的长度分割规则,但还是使用了错误的文字内容编码规则。这个最典型的就是 gb 编码和 big5 编码的问题。很多时候我们收到一个港澳台地区制作的文本文件,或者浏览这些地区的网站,会发现都是显示出一些奇怪的中文字,这就是因为这些地区的中文,都是用一个叫 big5 的编码规则来编码中文的,而大陆的电脑基本上都是默认按 gb2312 编码规则来显示中文,所以就会显示出错误的中文字符来了。
对不是代表文字的数据,按文字的方式来解读。如果你想试验一下这个,可以随便找一个图片文件,或者 exe 之类的,把文件名改成 “x.txt”,然后用 notepad(记事本)打开,你通常都会看到“乱码”。实际上在互联网上,很多不是文本格式(也就是说不仅仅包含文字)的页面,都可以在错误的情况下,被浏览器按照文本格式读取,自然就会出现乱码了。这也再一次证明,计算机实际上是“不认识”它要处理的内容的,你命令它以文本编码方式去解码一串数字,它就老老实实的去做了,至于解码出来是什么东西,它是不管的。这必须使用者或者程序员去关心。
计算机无法把文字信息显示出来。要理解这种故障的原因,我们除了了解计算机如果处理文字的编码外,还需要了解计算机是如何显示文字的。实际上任何的文字,都是一张小小的图片。一个横向 16 个点和纵向 16 个点的点阵图,就可以表示所有的英文字符和数字,而中文则需要更大更多点的图像来显示。计算机预先会存放所有这些字符的“图形”,然后根据文字的编码,显示出对应的图形。但是如果计算机没有对应编码的图像,就往往会显示出一些奇怪的字符,最常见的就是显示一个“?”来代替。而这些字符对应的一大批的“图形”,我们叫做“字库”。如果你要显示中文,就必须要有中文的字库,否则计算机不会知道如何“画”出一个字来。我们时常碰到的,在某些电脑上能显示正常,另外一些电脑上则显示乱码的,很有可能就是字库没有能正确安装。
事实上,对于计算机来说,它是意识不到“何为乱码”的,它只是按照既定的编码规则,去把一系列的数字处理成软件预定的内容而已。我们人类看起来无法解读的一堆字符,对于计算机来说和可以解读的正常字符,都是一样的——它们都是一串串的数字。
在整个世界,几乎都接受了ASCII作为英文文字的编码表,一直到现在这个编码规范还在默默的工作。然而中文文字就没有这么幸运了,大陆使用的 GB2312 编码规范和港台使用的 BIG5 就是对于中文文字不同的两套编码表。如果我们用 BIG5 的规则把一些汉字写入文件,比如“你好”,在文件了里记录的是数字 “42817 42606”,但是按 GB2312 编码规则读出的时候,却变成另外两个字,因为 GB2312 编码规则中的“你好”应该是 “5040347811” 来代表的。如果我们想把日文、韩文,或者别的一些文字混合到同一份文件里,情况将变得更加复杂——想要对一个序列中不同部分的数字,指定不同的编码表的话,你就为每一个代表字符的数字前面,都用另外一个数字来表示应该用哪份编码表。
另外一个更聪明的办法是,把世界上所有的文字,都统一到一份编码表里面来——这就诞生了Unicode。和前文的 ASCII 编码表相比,Unicode 编码表不仅仅为拉丁字母编码,同时也为全世界所有的文字都设计了编码。Unicode 至今仍在不断增修,每个新版本都加入更多新的字符。目前最新的版本为第六版,已收入了超过十万个字符(第十万个字符在 2005 年获采纳)。Unicode 发展是由非营利机构“统一码联盟”所负责的。
目前实际应用的 Unicode 版本叫 USC-2,使用 16 位的编码空间。也就是每个字符占用 2 个字节。这样理论上一共最多可以表示 2 的 16 次方个(即65536)个字符。基本能满足各种语言的需要。下图 1-11 所示就是用 Unicode 所显示的多国文字:
使用 Unicode 编码表来解决各种不同文字的混合显示问题的方法,展示了计算机以数字表达信息的一个通用原理:编码空间设计。如果要表达某种信息,这些信息里面,每个单元的“可能性”全集,决定了编码这个信息单元所需要的数字的长度。——因为英文字母只有几十个,所以使用 0~255 的数字就可以表示一个英文字母了。而中文字因为有上万个,所以就要用 0~65535 这么大的数字,才足够表达一个中文字。在计算机中,任何的信息,都遵守这个规律,比如要表达一副黑白图案,每个点只需要 0 和 1 两个数字就可以了,因此编码每个点的数字长度只需要用 0~1 这么小的数字,一连串的 0 和 1 就足够描述这个图案。而如果是彩色的图案,每个点所表现的颜色则需要要更长的数字来表达。就需要用一串 0~255 或者更大的数字来表达。
“用什么数字代表什么意思”的这个工作,就是所谓“编码表”,整个数字世界,就是由这样的各种“编码表”来规定各种各样信息所代表的数字的。
评论区
共 条评论热门最新