1. 程式人生 > >編碼的奧祕: ASCII碼和字元對映

編碼的奧祕: ASCII碼和字元對映

轉自:《編碼的奧祕》   第二十章

 

           數字計算機儲存器按位儲存,所以,需要在計算機上處理的資訊必須按位的形式儲存。我們已經知道如何用位來表示數和機器碼,下一個問題是如何用它來表示文字。畢境世界上大量堆積的資訊是文字形式的,就像裝滿圖書館的書、雜誌和報紙。儘管我們最終要用計算機來存放聲音、影象和電影資訊,但我們還是以較容易的文字存放開始。

           為了以數字形式表示文字,必須開發一些系統使得系統裡的每一個字母有唯一的編碼。文字中也存在數字和標點符號,所以也必須有它們的編碼。簡單地說,所有的字母、數字和符號都要編碼,這樣的系統叫作字元編碼集,每一個編碼叫作字元編碼。

          第一個問題是:這些編碼需要多少位?這並不是容易回答的問題。

          當考慮用位表示文字的時候,需要切合實際。我們習慣於看到書中、報刊、雜誌上精美的文字格式,段落按照相同的間隔整齊地分成一行一行,但這些並不是文字的本質。當我們在雜誌上看到一個小故事,幾年後在一本書中又看到同樣故事的時候,我們不會因為書中文字間距的不同而認為是不同的故事。

          換句話說,不要以這種印刷成行列的二維格式來看待文字,應該把文字看成是一維的字母、數字和標點符號流,此外,也許還有額外的編碼用來表示一段的結束和另一段的開始。

          再來看看,如果在雜誌上看到一個故事,後來又在書中看到同樣的故事但字樣有些不同,這是一個大問題嗎?如果雜誌上的寫法為

而書中的寫法為

這些差別難道是我們真正關心的嗎?恐怕不是。印刷樣式是微妙地影響了文字的觀感,但故事本身並沒有因為樣式的改變而不同。樣式可以經常修改,但不會帶來什麼影響。

           接下來另外一個簡化問題的方法是:用平版的文字。沒有斜體,沒有粗體,沒有下劃線,沒有顏色,沒有空心體,沒有上下標,沒有音調標記,沒有 Å、 é 、 、 等符號,只有9 9 %英語文本里純粹的拉丁字母。

          在對摩爾斯電碼和布萊葉盲文的早期研究中,可以看到如何將字母字元表示成二進位制的形式。儘管這些系統在特定的場合應用地很好,但用到計算機裡都有一些問題。例如:摩爾斯電碼是寬度可變的編碼:對常用的字符采用短編碼,對不常用的字符采用長編碼。這樣的編碼系統適用於電報,但對計算機來說卻不合適。另外,摩爾斯電碼對字母的大小寫沒有區分。

          布萊葉盲文是寬度固定的編碼,很適合計算機。每一個字元由 6位表示,也可以區分大小寫,儘管它是用特殊的 e s c a p e碼來區分的,該程式碼表明下一個字元為大寫。這也就是說,每個首部字元需要兩個程式碼而不是一個。數字用 s h i f t碼錶示,在這個特定的程式碼後緊跟的程式碼被看作表示數字,直到又一個 s h i f t碼將其轉換到字元狀態。

           我們的目標是開發一個字元編碼集,使得像如下的句子

可以用一串程式碼來表示,每一個程式碼具有一定的位數。一些程式碼用來表示字母,一些表示標點符號,一些表示數字。甚至有程式碼來表示字間的空格。上面的句子中有 1 8個字元(包括字間空格) ,這樣一個句子的連續字元程式碼常稱作文字串。

           在文字串裡,用程式碼來表示數字 (如2 7 )似乎很奇怪,因為前面許多章裡已講過用位來表示數字。我們可能會用簡單的二進位制數 1 0和111作為該句中 2和7的程式碼,但用在這裡是不合適的。該句中,字元 2和7可像英文作品中出現的任何一種字元一樣來看待 ,它們可能具有與它們的實際值毫不相干的字元程式碼。

           也許最經濟的字元編碼是 5位編碼,它首先用於 1 8 7 4年的電報機,是由法國電報服務公司職員 Emile Baudot 發明的。他的編碼 1 8 7 7年被服務公司採納,後來由 Donald Murray修改並在1 9 3 1 年被 C C I T T,即現在的國際電聯 (ITU) 標準化。該編碼的正式名稱是國際電報字母表N O . 2或I TA - 2,在美國通常稱為 B a u d o t,儘管更科學的叫法為 M u r r a y編碼。

         在2 0世紀, B a u d o t經常用於電傳打字機。 B a u d o t電傳打字機有一個鍵盤,除了只有 3 0個鍵和一個間隔棒外,有些像打字機。電傳打字機的鍵實際上是轉換器,它產生二進位制程式碼並且通過電傳打字機的輸出電纜一位緊接一位地傳送出去。電傳打字機也有列印機制,從電傳打字機的輸入電纜輸入的程式碼觸發電磁鐵在紙上打印出字元 。

         由於B a u d o t是5位編碼,所以總共只有 3 2個程式碼,這些程式碼的十六進位制值範圍從 0 0 h~1 F h。下表是 3 2個 程式碼所對應的字母表中的字元 :

         程式碼 0 0 h沒有指定。其餘的 3 1 個程式碼中, 2 6個指定給字母表中的字元, 5個用斜體字或短語表示出來了。

         程式碼0 4 h是空格程式碼,用來分隔不同的字;程式碼 0 2 h和0 8 h表示回車和換行。這些術語來自於電傳打字機。當在電傳打字機上打字並且到了一行的末尾時,按下一個槓桿或按鈕來完成兩件事情。第一,使列印頭回到開始處,以便從紙的左邊開始列印下一行,這是回車。第二,移動列印頭緊接至剛完成的那一行的下一行,這是換行。在 B a u d o t中,獨立的鍵產生這兩個程式碼。列印的時候, B a u d o t電傳打字機響應這兩個程式碼,完成相應動作。

         在B a u d o t系統裡,如何表示數字和標點符號呢?這就是程式碼 1 B h的作用,在表中標識為數字轉義。在數字轉義程式碼之後,所有的程式碼序列被看作是數字或標點符號,直到遇到字元轉義程式碼 ( 1 F h )再返回到字元狀態。下表是數字和標點符號的程式碼。

         實際上, I T U沒有定義程式碼 0 5 h、 0 B h和 1 6 h,而是保留為“國家使用”,這個表裡列出的是美國的用法。這些程式碼在某些歐洲國家語言中用作重音符號。響鈴程式碼用來敲響電傳打字機上能聽見的鈴聲;“ Who Are Yo u”程式碼啟用一種機制,用它電傳打字機能識別自己。

        像摩爾斯電碼一樣,這 5位編碼不能區別大、小寫。語句

        由下面的十六進位制資料流來表示:
       注意三個轉義程式碼 : 1 B h在數字的前面, 1 F h在數字的後面,最後一部分之前又有 1 B h。該行程式碼用回車、換行程式碼來結束。

       然而,如果一行兩次傳送該資料流到電傳印表機,將會出現以下情形:


       這是怎麼回事?印表機接收到的上一行的最後一個轉義程式碼是數字程式碼,所以第二行開始的程式碼被解釋成數字。、

       類似這樣的問題是採用轉義程式碼所產生的典型的令人煩惱的結果。儘管 B a u d o t是很經濟的編碼,但人們可能更想採用能唯一表示字元或標點符號且對大、小寫進行區分的程式碼。

       如果想確定比 B a u d o t更好的編碼系統需要多少位,只需把各種符號加起來:大小寫字母需5 2個程式碼, 0~9數字需 1 0個程式碼,這已經有 6 2個,加上一些標點符號,則超過了 6 4個程式碼,這意味著需要多於 6位的編碼。但是距離 1 2 8個字元數,似乎還有足夠的餘地。如果超過 1 2 8個字元,則需要 8位編碼。

       所以答案應該是7。如果不用轉換程式碼來區分大、小寫,那麼英文裡應該用 7位來表示字元。

       這些字元編碼都是什麼呢?當然,我們可以隨心所欲地編碼。如果打算自己製造計算機且計算機的每一個硬體都由自己製造,自己程式設計且不把自己所造的計算機去與任何其他計算機連線,則可以構造自己的編碼,所要做的就是給每一個字元一個唯一的編碼。

        但是因為很少有獨立製造和使用計算機這種情形發生,所以通常是大家遵循並使用同一編碼。這樣製造出來的計算機就可以與其他計算機相容,並且可以交換文字資訊。

        我們可能也不應該隨意編碼,例如,當在計算機上處理文字時,如果字母表上的字元是按順序進行編碼的,則會帶來很多好處,其碼這樣的順序使得按字母排序和分類更容易一些。

         幸運的是,我們已經有了這樣一個標準,即美國資訊交換標準程式碼,簡寫為 A S C I I碼。它1 9 6 7年正式公佈,此後一直是計算機工業界最為重要的標準。除了一個大的例外(在後面講到),可以肯定的是,無論什麼時候處理文字,總會以某種方式涉及到 A S C I I碼。

          A S C I I碼是 7位編碼,用二進位制程式碼 0 0 0 0 0 0 0~111111 1 ,即十六進位制程式碼 0 0 h~ 7 F h來表示。讓我們來看 A S C I I碼,但不要從最開始看,因為前 3 2個程式碼比其餘程式碼理解起來要困難一些。從第二批的 3 2個程式碼開始講起,它包括標點符號和 1 0個數字。下表列出了它們的十六進位制程式碼及對應的字元:

          
          注意2 0 h是空格符,用來分隔單詞和句子。

         接下來的 3 2個程式碼包括大寫字母和一些附加的標點符號。除 @符號和下劃線之外,這些符號在打字機上不經常出現,它們出現在標準的計算機鍵盤上:

         
        接下來的3 2個字元包括所有小寫字母和一些附加的標點符號,也是在打字機上不常出現的:
       注意該表中少了與 7 F h對應的最後一個字元。如果你一直在統計,這三個表總共列出了 9 5個字元。因為 A S C I I碼是7位編碼,可以有 1 2 8個程式碼,所以還有 3 3個程式碼可用。下面簡單地講一下這些程式碼。

       

可以表示成 A S C I I碼的十六進位制形式

         注意除了字母程式碼以外,還有逗號(程式碼 2 C)、空格(程式碼 2 0)和感嘆號(程式碼 2 1 )。下面是另一短句:

用 A S C I I碼錶示為:

注意句中數字 1 2表示成十六進位制數 3 1 h和 3 2 h,分別是數字 1 和 2的A S C I I碼。當數字 1 2作為文字流的一部分時,它不應該被表示成十六進位制碼 0 1 h和0 2 h,或者B C D碼1 2 h,或者十六進位制程式碼0 C h。這些程式碼在 A S C I I碼裡都表示的是其他意思。

         ASCII碼錶示的大寫字母與其對應的小寫字母的ASCII碼值相差20h,這使得編寫某些程式程式碼更為容易,如:把一個字串變成大寫。假設在記憶體的某個區域存放有字串,一個位元組放一個字元。下面的8080子程式假設字串的首地址存放在暫存器HL中;暫存器C存放有字串的長度,即字串中的字元個數:



          從小寫字母減去 2 0 h轉換成大寫字母的語句可以用下面的語句代替:

         A N I指令是一個“與”立即數的操作,它把累加器中的數值與 D F h(即二進位制數 11 0 11111)執行按位“與”操作,即把兩個數的對應位進行“與”操作“與”操作保留 A中的所有位,除了從左邊數第3位被置成0。把這個位設定為0也即把A S C I I碼錶示的小寫字母轉換成大寫字母。

       上面列出的 9 5個程式碼也稱作圖形字元 ,因為它們可以顯示出來。 A S C I I碼還包括 3 3個控制字元,它們不能顯示出來但表示執行某一特定功能。鑑於完整性,這裡列出了 3 3個控制字元,即使它們很難理解也不要擔心。在 A S C I I碼公佈以後,更多地是想把它們用在電傳打字機上,現在許多程式碼已經很少見到了。

         
          控制字元可以與圖形字元混合使用來設定一些基本的文字格式。這很容易理解,想像一下諸如電傳打字機或簡單印表機之類的裝置,它們對 A S C I I碼流作出的響應是在紙上打印出字元。裝置的列印頭通過列印一個字元並向右移動一格來對 A S C I I碼作出響應。上面這些很重要的控制字元就用來改變這種通常的動作。

          例如:看以下的十六進位制字串

0 9字元是一個水平製表符,簡稱 Ta b。假設列印頁面上所有的水平字元位置是從 0開始, Ta b的作用是在下一個水平位置即 8的倍數處開始列印下一個字元,如下所示:

這是保持字元按列對齊的簡便方法。

         換頁符( 1 2 h)的作用是使印表機跳過當前頁開始列印下一頁。

         退格符用來在一些舊的印表機上列印複合字符,例如,假設計算機要控制電傳打字機以重音標記來列印小寫字母 e,即è,可以通過用十六進位制碼 65 08 60來實現。

         最重要的控制字元是回車和換行,它們與 B a u d o t碼中的回車換行符意義相同。在印表機中,回車符使列印頭移到列印頁面的左邊,換行符使列印頭移到下一行,用兩個程式碼通常表示從新的一行開始。單獨使用回車符可以用來在一個已有的行上列印,單獨使用換行符可以用來跳到當前位置的下一行而不移到左邊。

         儘管 A S C I I 碼是計算機世界的主要標準,但在許多 I B M大型機系統上卻沒有采用。在S y s t e m / 3 6 0系統中, I B M研製了自己的 8位字元編碼,即擴充套件的 B C D交換程式碼 E B C D I C。該編碼是早期的 BCDIC 6 位編碼的擴充套件,從 I B M穿孔卡片使用的程式碼演變而來。穿孔卡片可以存放8 0個文字字元,這種模式由 IBM 1928年首先引入並且用了 5 0多年。
       當考察穿孔卡片與相關的 8位E B C D I C字元編碼的關係時,需要記住的是,在若干種不同技術的支援下這種程式碼已經經歷了好幾代的演變。正因為如此,不要指望從中發現太多的邏輯性和一致性。

       穿孔卡片中,字元編碼由一列上穿出的一個或多個矩形孔的組合而形成,字元本身通常在接近卡片的上邊沿處打印出來。下面的 1 0行由數字標識,分別是第 0行、第 1 行直到第 9行。在第0行之上沒有數字的行為第 11 行,最上邊的行為第 1 2行,沒有第 1 0行。

       以下是一些常用的 I B M穿孔卡片術語:行 0~ 9稱作數字行或數字穿孔,行 11 和 1 2稱作區域行或區域穿孔。有一些 I B M穿孔卡片也會帶來混淆,把行 0和9看作是區域行而不是數字行。

       一個8位E B C D I C字元編碼由高半位元組( 4位)與低半位元組組成。低半位元組是與字元的數字穿孔一致的 B C D碼,高半位元組是與區域穿孔一致的編碼。回憶一下第 1 9章的 B C D編碼標準,它是用二進位制編碼十進位制數,即用 4位編碼來代表十進位制的 0~9。

       對數字 0~ 9,沒有區域穿孔,沒有區域穿孔對應的高半位元組是 1111 ,低半位元組是數字穿孔的 B C D碼。下面是 0~9的E B C D I C編碼:

        對大寫字母,如果只有第 1 2行有穿孔,則用 11 0 0來標識;如果只有第 11 行有穿孔,則用11 0 1 來標識;如果只有第 0行有穿孔,則用 111 0來標識。大寫字母的 E B C D I C編碼為:

        注意這些編碼的編號次序。在一些場合,當用 E B C D I C文字編寫程式的時候,這些次序有時還真令人頭痛。

         小寫字母與大寫字母的數字穿孔相同但區域穿孔不同。小寫 a~i的第1 2行和第 0行有穿孔,相應的區域程式碼為 1 0 0 0; j~r的第1 2行和第 11 行有穿孔,區域程式碼為 1 0 0 1; s~z的第11 行和第0行有穿孔,區域程式碼為 1 0 1 0。小寫字母的 E B C D I C編碼為:

         
       當然,標點符號和控制字元也有 E B C D I C編碼,但對該編碼系統的全面考察並不需要。

       似乎I B M穿孔卡片上的每一列就足以提供 1 2位的編碼資訊,每個孔代表 1 位,是這樣嗎?果真如此的話,可以用穿孔卡片上每一列 1 2個位置中的 7個來表示 A S C I I碼的字元程式碼。但是,實際上,這並不合適,太多的穿孔將會影響到卡片物理狀態的平直。

       E B C D I C中的許多 8位碼沒有定義,建議採用 A S C I I碼的 7位編碼是合理的。當 A S C I I碼研製出來的時候,儲存器非常昂貴,於是一些人感到 A S C I I碼應該用 6位碼並且採用轉義字元來區分大小寫用以節約儲存器。這種觀點沒有被接受,相反,人們認為 A S C I I碼應該是 8位編碼,因為即使在當時人們也普遍認為計算機應該是按 8位儲存,而不是 7位。當然, 8位位元組現在是標準的。因此,儘管 A S C I I碼在技術上是 7位編碼,但它普遍是接 8位值來儲存的。

      位元組與字元之間的等價關係的確很方便,我們只需簡單地通過統計字元數就可以粗略估計一個文字檔案所需要的儲存空間。當然,用 K和 M來表示計算機儲存空間用得更為廣泛一些。

       例如,傳統的 8 . 5× 11英寸的雙倍空隙列印頁面有 1 英寸的頁邊空白和大約 2 7行的正文。每行約 6 . 5英寸寬,每英寸有 1 0個字元,這樣一頁共有 1 7 5 0個位元組。單倍空隙列印頁面大約是它的2倍,約 3 . 5 K B。

        《NEW Yo r k e r》雜誌每頁有 3列,每列有 6 0行,每行大約有 4 0個字元,這樣每頁有 7 2 0 0個字元(或位元組)。 《紐約時代》每頁有 6列。如果頁面都是文字而沒有標題和圖片(這是不常有的) ,則每列有 1 5 5行,每行大約有 3 5個字元,從而整個頁面有 32 550個字元,即 3 2 K B。

         當然,每本書的差別很大:

 

F. Scott Fitzgerald的 《The Great Gatsby》 大約300KB。
J. D. Salinger的 《Catcher in the Rye 》 大約400KB。
Mark Twain的《The Adventures of Huckleberry Finn》 大約540KB。
John Steinbeck 的《The Grapes of Wrath》 大約 1MB。
Herman Melville的《Moby Dick》 大約1.3MB。
Henry Fielding 的 《The History of Tom Jones》 大約 2.25MB。
Margaret Mitchell的 《 Gone With the Wind 》 大約 2.5MB。
Stephen King 的《 The Stand》 大約2.7MB。
Leo Tolstoy 的《 War and Peace 》 大約3.9MB。
Marcel Proust 的 《Remembrance of Things Post》 大約 7.7MB。


         美國國會圖書館大約有 20 000萬本書,總共有 2 0萬億字元,即 2 0 T B的文字資料。 (圖書館還有大量的照片和錄音。 )

         儘管 A S C I I碼是計算機世界裡最重要的標準,但它並不是完美的。 A S C I I碼的最大問題在於它太傾向於美國!的確, A S C I I碼即使對那些以英語為主要語言的國家也幾乎是不合適的。儘管 A S C I I碼包含有美元符號,但英鎊符號呢?還有許多西歐國家語言中用到的重音符號呢?更不用說在歐洲一些國家裡使用的非拉丁字母,包括希臘文、阿拉伯文、希伯來文和西里爾
文。此外,還有印度及東南亞國家用到的婆羅門教的手跡。而一個 7位編碼又如何來處理成千上萬的中文、日文、韓文筆畫以及韓語音節?

         在研究 A S C I I碼的時候,也一直在考慮其他國家的需要 ,儘管沒有充分考慮非拉丁字母。根據公開的 A S C I I碼標準, 1 0個A S C I I碼程式碼( 4 0 h、 5 B h、 5 C h、 5 D h、 5 E h、 6 0 h、 7 B h、 7 C h、7 D h和 7 E h)可用來重新定義而為某一國家使用。另外,如果需要,數字符號( #)可用英鎊符號(£)替換,美元符號( $)可用通用貨幣符號(¤)替換。顯而易見,只有使用包含這些替換符號的特定文字文件的所有人都知道這些變化的時候,替換符號才有意義。

         由於許多計算機系統按 8位來儲存字元,則可以設計擴充套件的 A S C I I碼字符集來包含 2 5 6個字元而不僅僅是 1 2 8個。在這樣的字符集裡,程式碼 0 0 h~ 7 F h定義成與 A S C I I碼一致;程式碼 8 0 h~F F h可定義成表示另外的字元。這種技術已被用來定義附加的字元程式碼,包含重音字母及非拉丁字母。作為例子,這裡有一個 9 6個字元的 A S C I I碼的擴充套件,稱之為第 1 號拉丁字母表,定義的字元編碼從 A 0 h~ F F h。在該表裡,十六進位制字元編碼的高半位元組由最高行給出,低半位元組由左邊列給出:

          

        程式碼A 0 h對應的字元為非斷開空格。通常計算機處理格式文字是按照行和段來編排的,每一行以空格符號斷開,對應的 A S C I I碼為2 0 h。程式碼 A0h 用來顯示一個空格,但不能用來斷開一行。非斷開空格可以用在如“ WW II ”這樣的文字中。程式碼 A D h定義成軟連字元,該連字元用來分開一個字中間的音節,且只在需要連線被兩行分開的一個單詞時才使用。

        遺憾的是,近幾十年來出現了許多不同的 A S C I I 碼的擴充套件,導致了混淆和不相容性。A S C I I碼通過擴充套件甚至可以編碼中文、日文和韓文的筆畫。有一個流行的編碼叫作 S h i f t - J I S(日本工業標準) ,其程式碼 8 1 h~ 9 F h用來表示 2位元組字元編碼的起始位元組。以這種方法, S h i f t J I S可編碼約 6 0 0 0個額外字元。遺憾的是, S h i f t - J I S不是使用這種技術的唯一系統。在亞洲,還有三個很流行的雙位元組字符集。

        雙位元組字符集有許多問題,不相容性只是其中之一。另一個問題是,一些字元—特別是普通的 A S C I I碼字元—是用1個位元組編碼來表示的,而成千上萬的筆畫則是由雙位元組編碼來表示,從而導致使用這樣的字符集很困難。

        在假定會有一個特定的字元編碼系統能適用於世界上所有語言的前提下, 1 9 8 8年,幾個主要的計算機公司一起開始研究一種替換 A S C I I碼的編碼,稱為 U n i c o d e。鑑於 A S C I I碼是 7位編碼, U n i c o d e採用 1 6位編碼,每一個字元需要 2個位元組。這意味著 U n i c o d e的字元編碼範圍從0 0 0 0 h~ F F F F h,可以表示 65 536個不同字元。對世界上所有可用計算機進行來通訊的語言來說,有足夠的擴充套件空間。

        U n i c o d e編碼不是從零開始的,開始的 1 2 8個字元編碼 0 0 0 0 h~ 0 0 7 F h與A S C I I碼字元一致。U n i c o d e編碼 0 0 A 0 h~ 0 0 F F h也與前面講到的對 A S C I I碼擴充套件的第 1 號拉丁字母表一致。其他世界範圍的標準也收編在 U n i c o d e中。

        儘管 U n i c o d e對現有的字元編碼做了明顯改進,但並不能保證它能很快被人們接受。A S C I I碼和無數的有缺陷的擴充套件 A S C I I碼已經在計算機世界中佔有一席之地,要把它們逐出計算機世界並不是件容易的事。

         對U n i c o d e來說的一個實實在在的問題是,它改變了一個文字字元與 1 個位元組儲存器之間的等效關係。用 A S C I I碼編碼, 《 The Grapes of Wr a t h》 這本書的大小約為 1M 位元組;而用U n i c o d e編碼,約是 2 M B,這也算是採用 U n i c o d e編碼付出的代價吧。