1. 程式人生 > >OneCoder翻譯 每個程式設計師必知的知識,UniCode和字符集(上)

OneCoder翻譯 每個程式設計師必知的知識,UniCode和字符集(上)

今天在處理了一個編碼的問題,激發了筆者強烈的弄清編碼問題的好奇心。遂先有了前面強烈推薦的文章:字元編碼介紹 通俗易懂 強烈推薦。下面是上篇文章中提到的延伸閱讀裡,第一篇文章的翻譯。水平有限,各位看官,勉強理解一下,錯誤之處還望指出。

你是否曾迷惑於Content-Type標籤?你知道你需要寫在HTML檔案中,但是卻不知道他是做什麼的。

你是否層收到來自保加利亞的朋友的郵件,主題卻是:”???? ?????? ??? ????”。

我曾經吃驚的發現有許多程式設計師沒有真正理解字符集,編碼,Unicode等相關問題的含義。若干年前,FogBUGZ的一個測試人員對他們是否可以處理日文的郵件感到好奇。日語?他們有日文郵件?這個我不清楚。當我近距離了解到我們層用來解析MIME

郵件的商業版的ActiveX系統時,我發現他對字符集的處理完全是錯誤的。所以,我們不得不去寫”誇張”的程式碼去恢復他做的錯誤的轉換,然後重做正確的轉換。當我發現,另一個商業版的包在字元的實現上也犯了同樣錯誤。我理解這個包的開發者的想法,他們”不能做任何事”。就像許多開發者一樣,他僅希望這個問題會莫名其表的消失。

但是事實不會那樣。當我發現,著名的web開發工具PHP,也幾乎完全忽略了字元編碼的問題,輕率的用8位表示字元,從而使得開發出 一個好的國際化的web應用幾乎不可能的時候,我認為夠了,這夠了。

所以在這裡,我宣告:如果你是一個工作在2003年的程式設計師,(OneCoder注:這篇文章寫於2003年。)並且你不知道基本的字元,字符集,編碼和Unicode

的知識,並且被我抓到,我將要懲罰你在潛艇裡撥洋蔥皮6個月,我發誓,我會做到。並且,更重要的是:他並不難。

在這篇文章裡,我將要告訴你每個開發人員必須知道的事情。告訴你,普通文字(plain text)=ascii=字元(characters) 都是8位的,不僅是錯誤的,而是無可救藥的錯誤的。並且,如果你仍在以這樣的理解程式設計,那較之於一個醫生不相信細菌有過之而無不及。在讀完這篇文章之前,不要在動手寫任何程式碼了。

在我開始之前,我需要提醒你,如果你是少數的瞭解國際化的人,你可能會覺得我整篇的討論都很淺顯。我確實盡力的試圖將門檻降到最低,以使每個人可以理解這是什麼並且能滿懷希望寫出處理各種語言的程式碼,而不僅僅是不包含音調的英文。同時,我還要提醒你,字元的處理僅僅是開發出國際化產品的一小部分工作,然而我一次只能說明一個問題,所以今天,我會講解字符集。

歷史回顧

最簡單的理解這些事情的方式自然是從頭去回顧。

追溯到Unix誕生,K&R發明了C語言的時代,任何東西都很簡單。EBCDIC 就是在那個環境下產生的。

當時包含的字元僅僅是,古老的無重音的英文字元,我們給這些字元以一些程式碼,稱為ASCIIASCII可以通過32-127之間的數字,來代表每一個字元。例如:空格(space)是32,A是65等等。這可以正好用7位進行儲存。在那個時代,大部分的計算機都是採用8位的,所以,你不僅僅可以儲存每一個可能的ASCII字元,你還可以有完整的一位去做任何你想做的事情:比如,WordStar(OneCoder注:可能是文書處理軟體),用最高位來標識最後一個單詞的最後一個字母,這也迫使WordStar僅可以處理英文文章。32以下的程式碼被成為不可列印的字元,是用來詛咒的。呵呵,開玩笑了。他們其實是操作字元,比如,程式碼7可以是你的計算機發出嘟嘟的聲音;12可以是換行等。

如果你是說英語的,那麼一切都很好。

因為一位元組有8位,很多人就會想,”我們可以用128-255來代表我們想要的東西”。不過我問題是,很多人都同時有同樣的想法,他們又都有自己對於128-255這些字元的規劃。IBM-PC創造了OEM字符集,可以表示一些帶有音調的歐洲語言和一些可以用來化字元的線,水平線,垂直線,帶有拐角的水平線等。你可以用這寫線條字元,繪製出一些整潔的表格和線條,你仍然可以在乾洗店裡那些8088系列的計算機裡看到這些字元。事實上,當美國以外的人們開始購買PC機開始,各種各樣的OEM字符集開始被設計出來,並且都是用高位的128個字元為自己所用。例如,在某些PC上,字元碼130代表é, 在以色列的一些計算機上,130代表希伯來字母ג。所以,當美國人傳送résumés給以色列人的時候,以色列人可能會收到rגsumגs。在很多情況下,比如俄羅斯,同樣有很多各種各種的設計針對高位的128個字元,所以你可能無法準確的翻譯俄羅斯文件。

終於,這種人人自由的OEM方式,隨著ANSI(OneCoder注:American Engineering Standards Committee)標註的出臺被終結。根據ANSI標準,大家都遵循低位的128字元的定義,跟ASCII的定義一樣,但是對於高位的128字元的處理,根據大家所處環境的不同,有各種各樣方式。這些不同的系統被稱作內碼表。(OneCoder注:原文為code pages,可能需要翻牆訪問)。因此,比如在以色列DOS使用的內碼表為862,希臘為737。他們都有相同的低128位的,但是高128位不同,儲存著各自有趣的字元。國際化版本的MS-DOS,有需要這樣的內碼表,處理了從英語到冰島語等語言,甚至有些多語言的內碼表,可以處理世界語和加利西亞語在同一臺計算機上。Wow。然而,想在同一個計算機裡處理希伯來語和希臘語是完全不可能的,除非你編寫自定義的程式,通過點陣圖來展示一切。因為,希伯來語和希臘語需要不同的內碼表對高的128位有不同的解釋。

同時,在亞洲,更誇張的是,亞洲的字母表有成千上萬的字元,完全不可能用8位字元去表示。不過,這個問題有一種不尋常的混亂的解決方式,DBCS(double byte character set),雙位元組字符集,在這個字符集中,有些字母用一個位元組儲存,有些用兩個。這使得對一個字串進行前移操作比較簡單,然而卻幾乎無法進行該死的後移操作。(OneCoder注:這裡的意思筆者也不是很明白。原文:It was easy to move forward in a string, but dang near impossible to move backwards.)。開發者被告之不要使用s++和s–進行後移和前移,而是呼叫Windows' AnsiNextAnsiPrev函式,這些函式知道該如何處理這些混亂的東西。

然而,很多人仍然假定一個位元組是一個字元並且一個字元是8位,只要你不將字串從一個計算機移到另一個或者只說一種語言,這些永遠是對的。然而,從Internet誕生那刻起,將字串從一個計算機傳遞到另一個計算機是非常司空見慣的事情,因此這些混亂的事情一下都發生了。幸運的是,Unicode誕生了。

Unicode

Unicode是一個勇敢的嘗試,用一個單一的字符集去包含這個星球所有合理編寫的系統甚至像克林貢語這樣假定出來的系統。一些人錯誤的認為Unicode是簡單的16位編碼,任何字元都需要用16位來表示,因此一共有65536個字元。這事實上是錯誤的。這是對Unicode最常見的誤解,所以如果你也是那樣認為的,不必難過。

事實上,Unicode有一種不一樣的方式去考慮字元的問題,你必須去理解Unicode的思考方式,否則你無法理解任何事。

到目前為止,我們已經假定,一個字母對映到一些儲存在磁碟或者記憶體中的位元組:A -> 0100 0001

Unicode中,字母A是非實際的。它在天堂中游離:A

這個虛幻的A不同於B,也不同於a,但是與A和A相同。這種不同字型的A是同樣的字元,但是與小寫的a不同的想法,看起來並不難以理解,但是在一些語言中恰恰指出了一個字母可能引起爭議的所在。德語中的ß是真實的字母還是僅僅是ss的有趣的書寫方式?如果一個詞尾的字母的外形發生改變,他是一個不同的字母嗎?希伯來語說是,阿拉伯語說不是。不管如何,Unicode聯盟中聰明的人們在過去十年中,在激烈的爭論中,想出瞭解決方案,你不必再去擔心那些事。他們已經完全解決了這個問題。

在任何字母表中的任何想象中的字元都被Unicode聯盟賦予一個魔數,比如:U+0639。這個魔數被成為字元碼(Onecoder 注:code point)。U+代表Unicode,數字是十六進位制的。U+0639 **是阿拉伯字母Ain。英語中的AU+0041。你可以在Windows2000/xp的字元對映工具或者訪問unicode**官網找到這些對映。

事實上,對於Unicode能表示的字元數沒有嚴格的限制,他們已經超過了65536,所以不是每個Unicode字母都可以壓縮到用兩個位元組表示,這很虛幻。

OK,現在我們有一個字串:Hello,在Unicode中對應5個字元碼:U+0048 U+0065 U+006C U+006C U+006F。是一組字元碼。我們還沒有提到任何關於這些字元碼在記憶體中如何儲存以及在email中如何展現的事。

編碼

這就是編碼的來歷。最初的關於Unicode的編碼想法是,我們就乾脆用兩個位元組儲存這些數字,這個想法導致了兩位元組的神話。因此,Hello變成了:00 48 00 65 00 6C 00 6C 00 6F

正確了?別這麼早下結論。難道它不可能是:48 00 65 00 6C 00 6C 00 6F 00 嗎?

好吧,從技術上講,可以,我相信他可以。事實上,早期的實現者想用high-endianlow-endian兩種模式儲存Unicode字元碼,不論哪種方式都是他們特定的CPU最快的處理方式。呵呵,夜以繼日,現在就有了兩種儲存Unicode的方式。

未完待續……