關于字符集和Unicode的相關知識

作者:Joel Spolsky 來源:轉載 時間:2010-03-15 類別:網頁理論

原文地址http://www.joelonsoftware.com/articles/Unicode.html
作者:Joel Spolsky
譯文:http://local.joelonsoftware.com/wiki/Talk:Chinese_(Simplified)

每個程序員都絕對必須知道的關于字符集和Unicode的那點兒事(別找借口!)

Unicode與字符集

你曾經是否覺得HTML中的"Content-Type"標簽充滿神秘?雖然你知道這個東西必須出現在HTML中,但對于它到底干嗎你可能一無所知。

你是否曾經收到過來自你保加利亞朋友的郵件,到處都是"???? ?????? ??? ????"?

我很失望,因為我發現許多軟件開發人員到現在為止都還沒有對字符集、編碼、Unicode有一個清晰的認識,這是個事實。幾年前,在測試FogBUGZ項目時,忽然想看看它能不能接收用日文寫的電子郵件。這個世界上會有人用日文寫電子郵件?我不知道。測試結果很糟糕。我仔細看了用來解析MIME (Multipurpose Internet Mail Extenisons)格式的郵件所用的ActiveX控件,發現了它在字符集上面做的蠢事。于是我們不得不重新寫一段代碼,先消除Active控件的錯誤,然后再完成正確的轉換。類似的事情在我研究另一個商業庫的時候同樣發生了,這個庫關于字符編碼這部分的實現簡直糟透了。我找到它的開發者,把存在問題的包指給他,他卻表示對于此無能為力。像很多程序員一樣,他只希望這個缺陷會被人們遺忘。

事實并非如他所愿。因為我發現,像PHP這么流行的網頁開發工具,竟然在實現上也完全忽略了多種字符編碼的存在(譯者注:這篇文章寫于2003年,現在的 PHP可能已經糾正了這個問題吧),盲目地只使用8個比特來表示字符,于是開發優秀的國際化的Web應用程序變成了一場夢。我想說,受夠了。

我申明:在2003年,如果你是一個程序員,但你卻對字符、字符集、編碼和Unicode一無所知,那么你別讓我抓到你。如果落在我手里,我會讓你待在潛水艇里剝六個月的洋蔥,我發誓。

另外,還有一件事:

這個一點都不難。

在這篇文章里,我所講的是每一個工作中的程序員都應該知道的知識。所有以為"純文本 = ASCII碼 = 一個字符就是8比特"的人不單單錯了,而且錯得離譜。如果你仍然堅持使用這種方式編寫程序,那么你比一個不相信細菌的存在醫生好不到哪里去。所以在你讀完這篇文章以前,不要再寫半行代碼。

在我開始之前,必須說明白,如果你已經了解了國際化,可能你會覺得這篇文章過于簡單。沒錯,我的的確確是想架一座最短的橋,讓任何人都可以理解發生了什么事,懂得如何寫出可以在非英文語言環境是正常工作的代碼。還得指出,字符處理僅僅是軟件國際化中的一小部分,但一口吃不成個胖子,今天我們只看什么是字符集。

歷史回顧

可能你以為我要開始談非常古老的字符集如EBCDIC之類的,實際上我不會。EBCDIC與你的生活無關,我們不需要回到那么遠。

關于字符集和Unicode的相關知識

回到一般遠就行了。當Unix剛出來的時候,K&R寫了《The C Programming Language》一書,那時一切都很簡單。EBCDIC已經慚慚不用,因為需要表示的字符只有那些不帶重音的英文字母,ASCII完全可以勝任。ASCII使用數字32到 127來表示所有的英文字母,比如空格是32,字母"A"是65等等。使用7個比特就可以存儲所有這樣字符。那個時代的大多數計算機使用8個比特來,所以你不但可以存儲全部的ASCII,而且還有一個比特可以多出來用作其他。如果你想,你可以把它用作你不可告人的目的。32以下的碼字是不可打印的,它們屬于控制字符,像7表示響鈴,12表示打印機換紙。

所有的一切都看起來那么完美,當然前提你生在一個講英文的國家。

關于字符集和Unicode的相關知識

因為一個字節有8個比特,而現在只用了7個,于是很多人就想到"對呀,我們可以使用128-255的碼字來表示其他東西"。麻煩來了,這么多人同時出現了這樣的想法,而且將之付諸實踐。于是IBM-PC上多了一個叫OEM字符集的東西,它包括了一些在歐洲語言中用到的重音字符,還有一些畫圖的字符,比如水平線、垂直線等,水平線在右端會帶一個小彎鉤,垂直線會如何等等。使用這些畫圖字符你可以畫出漂亮的框、畫出光滑的線條,在老式的烘干機上的8088電腦上你依然可以看到這些字符。事實上,當PC在美國之外的地方開始銷售的時候,OEM字符集就完全亂套了,所有的廠商都開始按照自己的方式使用高128個碼字。比如在有些PC上,130表示é,而在另外一些在以色列出售的計算機上,它可能表示的是希伯來字母ג,所以當美國人把包含résumés這樣字符的郵件發到以色列時,就為變為rגsumגs。在大多數情況下,比如俄語中,高128個碼字可能用作其他更多的用途,那么你如何保證俄語文檔的可靠性呢?

最終ANSI標準結束了這種混亂。在標準中,對于低128個碼字大家都無異議,差不多就是ASCII了,但對于高128個碼字,根據你所在地的不同,會有不同的處理方式。我們稱這樣相異的編碼系統為碼頁(code pages)。舉個例子,比如在以色列發布的DOS中使用的碼頁是862,而在希臘使用的是737。它們的低128個完全相同,但從128往上,就有了很大差別。MS-DOS的國際版有很多這樣的碼頁,涵蓋了從英語到冰島語各種語言,甚至還有一些"多語言"碼頁。但是還得說,如果想讓希伯來語和希臘語在同一臺計算機上和平共處,基本上沒有可能。除非你自己寫程序,程序中的顯示部分直接使用位圖。因為希伯來語對高128個碼字的解釋與希臘語壓根不同。

同時,在亞洲,更瘋狂的事情正在上演。因為亞洲的字母系統中要上千個字母,8個比特無論如何也是滿足不了的。一般的解決方案就是使用DBCS- "雙字節字符集",即有的字母使用一個字節來表示,有的使用兩個字節。所以處理字符串時,指針移動到下一個字符比較容易,但移動到上一個字符就變得非常危險了。于是s++或s--不再被鼓勵使用,相應的比如Windows下的AnsiNext和AnsiPrev被用來處理這種情況。

可惜,不少人依然堅信一個字節就是一個字符,一個字符就是8個比特。當然,如果你從來都沒有試著把一個字符串從一臺計算機移到另一臺計算機,或者你不用說除英文以外的另一種語言,那么你的堅信不會出問題。但是互聯網出現讓字符串在計算機間移動變得非常普遍,于是所有的混亂都爆發了。非常幸運,Unicode適時而生。

Unicode

Unicode 是一個勇敢的嘗試,它試圖用一個字符集涵蓋這個星球上的所有書寫系統。一些人誤以為Unicode只是簡單的使用16比特的碼字,也就是說每一個字符對應 16比特,總共可以表示65536個字符。這是完全不正確的。不過這是關于Unicode的最普遍的誤解,如果你也這樣認為,不用感到不好意思。

事實上,Unicode使用一種與之前系統不同的思路來考慮字符,如果你不能理解這種思路,那其他的也就毫無意義了。

到現在為止,我們的做法是把一個字母映射到幾個比特,這些比特可以存儲在磁盤或者內存中。

A -> 0100 0001

在Unicode中,一個字母被映射到一個叫做碼點(code point)的東西,這個碼點可以看作一個純粹的邏輯概念。至于碼點(code point)如何在內存或磁盤中存儲是另外的一個故事了。

在Unicode中,字母A可看做是一個柏拉圖式的理想,僅存在于天堂之中:(我的理解是字母A就是一個抽象,世界上并不存在這樣的東西,如果數學里面的0、1、2等一樣)

A

這個柏拉圖式的AB不同,也與a不同,但與AA相同。這個觀點就是Times New Roman字體中的A與Helvetica字體中的A相同,與小寫的"a"不同,這個應該不會引起太多的異議。但在一些語言中,如何辨別一個字母會有很大的爭議。比如在德語中,字母 ß是看做一個完整的字母,還是看做ss的一種花式寫法?如果在一個字母的形狀因為它處在一個單詞的末尾而略有改變,那還算是那個字母嗎?阿拉人說當然算了,但希伯來人卻不這么認為。但無論如何,這些問題已經被Unicode委員會的這幫聰明人給解決了,盡管這花了他們十多年的時間,盡管其中涉及多次政治味道很濃的辯論,但至少現在你不用再為這個操心了,因為它已經被解決。

每一個字母系統中的每一個柏拉圖式的字母在Unicode中都被分配了一個神奇的數字,比如像U+0639。這個神奇數字就是前面提到過的碼點(code point)。U+的意思就是"Unicode",后面跟的數字是十六進制的。U+0639表示的是阿拉伯字母Ain。英文字母A在Unicode中的表示是U+0041。你可以使用Windows 2000/XP自帶的字符表功能或者Unicode的官方網站(www.unicode.org)來查找與字母的對應關系。

事實上Unicode可以定義的字符數并沒有上限,而且現在已經超過65536了。顯然,并不是任何Unicode字符都可以用2個字節來表示了。

舉個例子,假設我們現在有一個字符串:

Hello

在Unicode中,對應的碼點(code point)如下:

U+0048 U+0065 U+006C U+006C U+006F

瞧,僅僅是一堆碼點而已,或者說數字。不過到現在為止,我們還沒有說這些碼點究竟是如何存儲到內存或如何表示在email信息中的。

12
标签: Unicode

相關文章:

推薦設計

最新文章