奎克的藏经阁 · MinerU 解析内容

编码 隐匿在计算机软硬件背后的语言

438 个 PDF 物理页 · 本页显示文件第 331–340 页

PDF 物理页索引 330

文件中的第 331 页 · 印刷页码:314

再接下来的32个编码是所有小写字母和一些附加的标点符号及其对应的十六进制编码,这些字符也很少在打字机上出现。

注意,表的最后不包括7Fh及其对应的字符。如果你统计一下,就会发现这三张表共涵盖了95个字符。由于ASCI码的编码长度为7位,所以最多可以表示128个编码,这样算下来还剩33个编码可用。下面我们通过几个简短的例子学习一下编码。

编码隐匿在计算机软硬件背后的语言

314

PDF 物理页索引 331

文件中的第 332 页 · 印刷页码:315

像这样一段字符串:

$$
H e l l o , y o u !
$$

转换成ASCII码,用十六进制数表示如下:

$$
4 8 6 5 6 C 6 \mathrm { F } 2 \mathrm { C } 2 0 7 9 6 \mathrm { F } 7 5 2 1
$$

这段编码中,除了普通的字符,逗号(编码2C)、空格(编码20)和感叹号(编码21)容易遗漏,需要额外注意。我们再来看一个例子:

$$
I a m \ I 2 y e a r s o l d .
$$

它用 ASCII 码表示为:

49 20 61 6D 20 31 32 20 79 65 61 72 73 20 6F 6C 64 2E

有意思的是数字12的表示方法。在这段编码串中,它被表示成十六进制数31h和32h,也就是数字1和2的ASCII码的组合。当数字12以文本流的身份出现时,不应该用十六进制码 01h和 02h,或者BCD码12h,或者0Ch来表示。因为这些编码在 ASCII码中表示其他的意思。

在 ASCII码中,一个大写字母与其对应的小写字母的ASCII码值相差20h。这种规律大大简化了程序代码的编写,例如一段将特定的字符串变成大写的程序。假设有一个字符串存放在内存的某个区域,每个字符占据一个字节。下面是一段8080子程序,初始状态下字符串的首地址存放在寄存器HL中;寄存器C存放字符串的长度,也就是字符的个数。

Capitalize: MOV A, C ;c表示剩余的字符数CPI A, 00h ;与0进行比较JZ AllDone ;如果剩余的字符数为0,程序结束MOV A, [HL] ;取得下一个字符CPI A, 61h ;判断A代表的字符的ASCII码是否比a'小JC Skipit ;如果比a'小,就跳过CPI A, 78h ;判断是否比′z′大JNC SkipIt ;如果是,则跳过SBI A, 20h ;判断是否是小写,如果是,则减20hMOV [HL], A ;保存修改过的字符
SkipIt: INX HL ;指向下一个字符DCR C ;计数器减一

20 ASCII 码和字符转换

315

PDF 物理页索引 332

文件中的第 333 页 · 印刷页码:316

JMP Capitalize ;返回到程序起始处

AllDone: RET

还有另外一种方法也可以将小写字母减去20h而转换成大写字母,如下所示:

ANI A, DFh

ANI指令(ANDImmediate)用来“与”一个立即数。在上面这个例子中,累加器中的数值与DFh执行“按位与”操作,其中DFh转换成二进制数就是11011111。“按位与”操作就是把两个数分别转换成二进制,然后将对应的位进行“与”操作。这个例子中,除了自左向右数的第3位被置成0外,A中的其他位均被保留。通过将这一位设置为0,我们实现了将小写字母的ASCI码转换成大写字母的目的。

编码隐匿在计算机软硬件背后的语言

316

PDF 物理页索引 333

文件中的第 334 页 · 印刷页码:317

前面讲到的 95个编码也被称为图形文字(graphic characters),因为它们可以被显示出来。其实 ASCI码还包含 33 个控制字符(control characters),它们用来执行某一特定功能,因而不用显示出来。为了完整地讨论ASCI编码,下面将这33个控制字符也列举了出来,有一些的确很难理解,不过不用在意。其实在ASCI码公布以后,当时人们更多的是想把它用在电传打字机上,所以,如今其中的许多编码已经渐渐离开了人们的视线。

人们最初的想法是可以在图形字符中使用控制字符,以便对文本格式进行基本的调整。举个例子或许会帮助你更好地理解这种做法:假如有一台电传打字机或者打印机,它负责解析ASCⅡ码,解析之后做出相应的操作,最后在纸上打印出字符。设备的打印头每打印一个字符就向右移动一格,通过这种方式来对ASCII码做出响应。而要实现这些操作就需要用到控制字符。

举个例子来讲,看看下面这个十六进制字符串:

41 09 42 09 43 09

编码09代表水平制表符,简写为Tab。假设打印的过程中,所有水平排列字符的起始位置都为0,Tab的作用是在下一个水平位置即在距前一个字符的间距为字符长度8倍的位置打印下一个字符,如下所示:

这种简单有效方法使得字符可以保持按列对齐。

有一些控制字符甚至沿用至今,例如换页符(12h),它使得打印机跳出当前页,并

20 ASCII 码和字符转换

317

PDF 物理页索引 334

文件中的第 335 页 · 印刷页码:318

开始准备打印下一页。

回退符可以用来打印复合字符,尤其是在一些旧的打印机上。假设计算机要控制电传打字机,使其不仅打印小写字母e,还要将其重音标记出来,即è。我们可以使用回退符来实现,十六进制码为:650860。

计算机中的回车和换行与Baudot码中表示的意思相同,它们可以算得上是控制符中最重要的两个符号。在打印机中,回车符使得打印头换行并转移至当前页面的最左端,换行符使打印头转移至当前位置下一行。这两种操作都使得打印头移至新的一行。回车符通常用来另起一行继续打印,换行符通常在不需要移到页面最左端而换行时使用。

尽管ASCII码在计算机领域可以说是一统江湖,但许多IBM大型机上却没有采用这种标准。例如,System/360产品内部采用的是IBM自发研制的8位字符编码系统,也被称为扩展的 BCD 交换码(Extended BCD Interchange Code),或 EBCDIC(英文中的发音为EBB-see-dick)。EBCDIC 是早期的 6 位 BCDIC 编码的扩展形式,BCDIC 的起源于 IBM的打孔卡。一张打孔卡——存储容量为80个文本字符1928年由IBM首创并沿用了将近50年,它的外观如下图所示。

在考虑打孔卡与8位EBCDIC字符码的关系时,需要知道,在几代技术的影响下,这种编码也历经几十年的演变。因此,打孔卡与EBCDIC之间的逻辑性和一致性也逐渐消失了。

打孔卡上每一列穿出的一个或多个矩形孔代表一个字符,而这些字符一般也打印在卡片的顶部。最下面的10行由数字标识,自上向下分别为第0行、第1行直到第9行。第0行上面的一行通常不出现数字,称为第11行,顶端为第12行,这里没有第10行。

以下面列举一些IBM打孔卡的常用术语:第0\~9行称做数字行(digit rows)或数字穿孔(digit punches),第 11 和 12 行被称做区域行(zone rows)或区域穿孔(zone punches)。由于不统一,IBM打孔卡用起来有时会有些混乱:比如有的卡片把第0和第9行看做是区域行而不是数字行。

编码——隐匿在计算机软硬件背后的语言

318

PDF 物理页索引 335

文件中的第 336 页 · 印刷页码:319

一个EBCDIC字符码由8位比特组成,进一步可以细分为高半字节(4比特)与低半字节。低半字节是BCD码,与字符的数字穿孔保持一致,高半字节与区域穿孔的编码保持一致(而且与区域穿孔一一对应)。回忆一下第19章的BCD编码原理,其本质是采用二进制数对十进制数进行编码(binary-coded decimal)—其中数字0\~9都利用不同的4位二进制数进行编码。

数字0\~9并不需要区域穿孔进行额外表示,它们的EBCDIC编码的高半字节是1111,代表了区域穿孔不起作用,而0\~9的EBCDIC编码的低半字节是数字穿孔的BCD码,如下所示。

大写字母有一些有趣的规律,如果区域穿孔只出现在第12行,则高半字节标识为1100;如果只出现在第11行,则高半字节标识为1101;如果出现在第0行,则高半字节标识1110。下表给出了大写字母及其对应的EBCDIC编码。

20 ASCII 码和字符转换

319

PDF 物理页索引 336

文件中的第 337 页 · 印刷页码:320

续表

值得注意的是R与S之间编号有跳变。有时在编写程序的时候,尤其是程序中用到EBCDIC编码时,这个容易被忽视的小细节往往会令人抓狂。

小写与大写字母的数字穿孔是相同的,但它们的区域穿孔不同。在a\~i的小写字母,穿孔位于第12行和第0行,高半字节对应的编码为1000;在j\~r的小写字母,穿孔位于第12行和第11行,高半字节对应的编码为1001;在s\~z的小写字母,穿孔位于第11行和第0行,高半字节对应的编码为1010。小写字母的EBCDIC字符及其对应的十六进制编码如下表所示。

当然,标点符号和控制字符也都有自己的EBCDIC编码,但对于这些字符的编码系统没有必要去深究。

仔细观察IBM打孔卡,其中每一列细细数下共有12个孔,每个孔代表1位,也就是说可以提供12位的编码信息,不是吗?我们其实可以用打孔卡上每一列12孔中的7个来表示ASCI码。但是,这种方案有一个非技术方面的缺陷,那就是太多的穿孔将使得卡片变得很脆弱,容易折断。

采用8位编码的EBCDIC中其实还有很多编码未定义,这也说明当年 ASCII码采用了7位编码也是合乎情理的。在ASCI码刚刚问世的那个年代,存储器的价格贵得令人咋舌,有一些观点认为ASCI码可以用6位编码并配合转义字符来使用,这样既可以区分大小写又节约了存储器。这种方案并没有被采纳,当时还有一些人认为ASCI码应采用8位编码,他们对计算机的体系结构有了一个大胆的推测,即计算机应该按字节存储,7位存储是不合适的。今天来看,8位的字节存储已经作为了一项标准。尽管ASCI码从技术的本质上来看是7位编码,但仍以8位的形式存储。

编码隐匿在计算机软硬件背后的语言

320

PDF 物理页索引 337

文件中的第 338 页 · 印刷页码:321

在字节与字符之间建立一种等价关系大大简化了我们的工作,举例来讲,如果要粗略估计一个文本文件所需要的存储空间,只要统计字符数就可以了。这时前面学过的K(kilos)和M(Megas)就派上了用场,用它们来表示文本所占据的计算机存储空间更加通俗易懂。

传统的排版格式是:一张大小为8.5×11英寸的打印纸,采用双倍行距,1英寸的页边距,每页可以容纳约27行的正文。每行宽度约为6.5英寸,每英寸可容纳10个字符,通过计算可以知道每页共包含约1750个字节。如果页面采用单倍行距,那么打印纸的容量约为原先的2倍,即3.5KB。

翻开一本《纽约客》(The NewYorker)杂志,可以看到杂志每页有3栏,每栏包含60行,每行约有40个字符,这样算下来每页大致包含7200个字符(也可以说成字节)。

《纽约时报》(New York Times)每一页包含6栏。假如页面都是文字而不包含标题和图片(这其实是不大可能的),那么可以认为每栏包含155行,每行大约容纳35个字符,这样算下来整个页面共包含32,550个字符,即32KB。

一般来讲精装书每页大约包含500个单词。根据统计,每个单词平均占用5个字母一一更确切地来讲应该是6个字母,因为单词与单词之间是通过空格来分隔的,所以要一并统计在内。这样算下来,书的每一页大约包含3000个字符。假设每本书平均页数为333,这个估计或许和实际不符,但如果这样估算的话,每本书平均容量为1MB。

不得不承认的是,书与书之间千差万别,所以上面这些也只是估算,下面列举出一些实际数据。

斯科特·菲茨杰拉德(F.Scott Fitzgerald)的《了不起的盖茨比》(The Great Gatsby)大约 300 KB。

塞林格(J.D. Salinger)的《麦田守望者》(Catcher in the Rye)大约 400 KB。

马克·吐温(Mark Twain)的《哈克贝里·弗恩历险记》(The Adventures of Huckleberry

20 ASCII 码和字符转换

321

PDF 物理页索引 338

文件中的第 339 页 · 印刷页码:322

Finn)大约 540 KB。

约翰·斯坦贝克(John Steinbeck)的《愤怒的葡萄 / 怒火之花》(The Grapes of Wrath)大约1MB。

赫尔曼·梅尔维尔(Herman Melville)的《白鲸》(Moby Dick)大约1.3 MB。

亨利·菲尔丁(Henry Fielding)的《弃儿汤姆·琼斯的历史》(The History of Tom Jones)大约 2.25 MB。

玛格丽特·米切尔(Margaret Mitchell)的《乱世佳人》(GoneWith theWind)大约2.5 MB。

斯蒂芬·金(Stephen King)的《末日逼近》(The Stand)大约 2.7 MB。

列夫·托尔斯泰(Leo Tolstoy)的《战争与和平》(War and Peace)大约 3.9 MB。

马塞尔·普鲁斯特(Marcel Proust)的《追忆似水年华》(Remembrance of Things Post)大约 7.7 MB。

美国国会图书馆(The United States Library of Congress)藏书约为 2000 万本,大概有20万亿字符,从存储器角度来说,数据总量为20TB(这还不包括图书馆中的大量珍贵照片和录音资料)。

尽管ASCI码是计算机领域最重要的标准,但它并不是十全十美的。它的问题就蕴含在它的全称中—American Standard Code for Information Interchange,它是太美国化了!即使那些以英语为主要语言的国家,ASCII码也并不适用。ASCII码中包含美元符号,而英镑符号怎么找不到呢?还有西欧国家语言中用到的重音符号在哪里?更别说使用非拉丁字母的希腊文(Greek)、阿拉伯文(Arabic)、希伯来文(Hebrew)和西里尔文(Cyrillic)等欧洲国家了。此外,印度及东南亚地区用到的婆罗门手记、北印度的Devanagari方言、孟加拉语、泰语、西藏语也并没有在ASCII码中出现。简单的7位编码在面对数以万计的中国、日本、韩国的象形文字,以及奇怪的朝鲜文音节时也显得力不从心。

在ASCⅡ码的发展历程中,尽管没有在引入非拉丁字母方面做过工作,但开发者也一直在积极思考与改进编码系统,使其适用于其他国家。根据公布的ASCⅡ码标准,有10个ASCII码保留位(40h、5Bh、5Ch、5Dh、5Eh、60h、7Bh、7Ch、7Dh和7Eh)可被重新定义,这样就便于特定国家的使用。另外,英镑符号(£)可以在需要时替换特殊符号(#),通用货币符号(α)可以在需要时替换美元符号(\$)。当然,为使得这一替换过程不发生混淆,如果在文本文件中使用了这些重定义的符号,相关人员都必须知道这些变化。

编码隐匿在计算机软硬件背后的语言

322

PDF 物理页索引 339

文件中的第 340 页 · 印刷页码:323

大多数计算机系统采用8位编码来存储字符,我们也自然地想到设计一种扩展的ASCI字符集,这样可以包含256个字符,比原先扩展了一倍。在这种字符集中,编码00h\~7Fh与原 ASCI码保持一致;编码 80h\~FFh可以用来引入其他字符。这项技术已经被用来定义附加的字符编码,比如前面提到过的重音字母以及非拉丁字母。下面这个例子是对 96 个额外字符的 ASCI 码扩展,称为第 1 号拉丁字母表(Latin Alphabet No.1),其中包括A0h\~FFh字符编码。在该表中,每个字符的十六进制编码的高半字节由第一行给出,低半字节由第一列给出,如下表所示。

编码 A0h对应的字符为不中断空格(No-Break Space)。通常计算机在对文本进行排版时,会将其划分为行和段,行与行之间以空格符号区分(空格所对应的ASCII码为20h)。编码A0h显示为空格,但是并不表示行与行之间被断开。比如在“WWⅡ”这样一段文字中就可以使用不中断空格。编码ADh被定义为软连字符(soft hyphen),它的用途是连接同一单词之间的音节,在一个单词被不得已划分在两行时就会用到它。

20 ASCII 码和字符转换

323