1.10 表示文本
此时,你已经知道了位是我们在计算机中全部工作的基础,我们可以用位来表示其他的东西,比如数字。是时候介绍新的东西了:用数字表示其他内容,比如字母和键盘上的符号。
1.10.1 ASCII
就像数字表示法一样,也存在几个互相竞争的文字表示法。1963年,美国信息交换标准代码(American Standard Code for Information Interchange, ASCII)从一些文字表示法中脱颖而出,它为键盘上的所有符号分配了7位的数字值。例如,65表示大写的A,66表示大写的B,以此类推。IBM的扩展二进制编码的十进制交换码(Extended Binary-Coded Decimal Interchange Code, EBCDIC)没有竞争过ASCII码,它基于用于穿孔卡片机的编码。EBCDIC中的“BCD”部分代表的是前面提到的二进制编码的十进制数。ASCII码表如表1-11所示。
从表1-11中找到字母A,可以看到它的十进制值是65,十六进制值是0x41,也就是八进制的0101。事实证明,由于历史原因,ASCII字符代码还是常用八进制。
表1-11 ASCII码表
ASCII表中有一些很有趣的编码。它们被称为控制字符,因为相对于输出来说,ASCII字符是控制东西的。表1-12展示了它们所代表的内容。
表1-12 ASCII控制字符
其中许多控制字符被用于通信控制。例如,ACK表示“我收到了消息”,NAK表示“我没有收到消息”。
1.10.2 其他标准的演变
ASCII码只使用了一段时间,因为它只包含英语所需的字符。早期的计算机大多产自美国,其他的则产自英国。随着计算机的普及,其他语言的支持需求也越来越大。国际标准化组织(International Standards Organization, ISO)采用了ISO-646标准和ISO-8859标准,这两个标准基本上还是ASCII,只不过扩展了一些欧洲语言中使用的重音符号和其他双音符号。日本工业标准(Japanese Industrial Standards, JIS)委员会提出了日文字符专有的JIS X 0201。此外,还有中文标准、阿拉伯文标准等。
产生这些不同标准的原因之一是,当时的位成本比现在高得多,所以7、8个位被打包表示字符。随着位成本的下降,人们提出了一个较新的标准Unicode,将16位编码分配给了字符。当时,人们认为16位足以容纳地球上所有语言的所有字符,甚至还有剩余空间。后来Unicode已经扩展到了21位(其中有1 112 064个有效值),我们认为21位才能满足对所有字符的表示,但考虑到我们喜欢创造新的表情符号,标准可能不会持久不修改。
1.10.3 UTF-8
计算机使用8位来存储一个ASCII字符,因为计算机的设计决定它不适合处理7位的数量。同样,虽然位成本比过去低廉了很多,但也不至于便宜到随便使用16位来存储一个只需要用8位的字母。Unicode通过为字符代码提供不同的编码来解决这个问题。编码是代表另一个位型的位型。我们用位这样的抽象概念来创建代表字符的数字,然后用其他数字来代表这些数字。你明白我说的“假设”是什么意思了吧?有一种特别的编码叫作UTF-8(Unicode Transformation Format-8 bit),由美国计算机科学家Ken Thompson和加拿大程序员Rob Pike提出。我们最常使用UTF-8编码,因为它具有高效率和向后兼容性。UTF-8编码使每个ASCII字符占8位,这样就不会占用ASCII数据的额外空间。它对非ASCII字符进行编码的方式不会破坏使用ASCII码的程序。
UTF-8将字符编码为一个8位块序列,通常称为八位字节。UTF-8的一个巧妙之处是,第一块中最重要的数对应序列的长度,因此很容易识别出第一块。序列的长度很有用,因为它允许程序很容易找到字符边界。ASCII字符都是7位的,所以每一个字符只需要一个大块,这对说英语的人来说很方便,因为它比其他需要非ASCII符号的语言更紧凑。
从图1-16中可以看到,字母A的数字编码在ASCII码和Unicode码中是相同的。为了用UTF-8编码A,不管是什么编码,只要适合7位的编码,都会得到一个UTF-8块,MSB设置为0。这就是UTF-8中字母A有前导0的原因。π符号的Unicode不适合7位编码,但适合11位编码。为了用UTF-8对π编码,我们使用2个8位块,第一个块以110开头,第二个块以10开头,这样每个块分别剩余5个位和6个位来保存剩余的编码。最后,我们看到的Unicode是16位的,所以需要3个UTF-8块。
图1-16 UTF-8编码示例