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

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

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

所以人們不得不在每個Unicode串前強制的加上奇怪的約定:FE FF,這被稱作Unicode位元組順序標識(OneCoder注:Unicode Byte Order Mark)。如果你交換了高低位的順序,那將變成FF FE,讀取你字串的也將知道需要交換每一個位元組。啊,不過不是每個Unicode字串的開頭都有位元組順序標識。

曾經,人們認為這足夠好了,但是有些開發者卻開始抱怨,看那一堆一堆的的0。是啊,因為他們是美國的開發者,他們僅關注英語文字,很少使用U+ooFF

以上的編碼。當然,這也是因為他們是來自加利福尼亞的對此不屑的自由的嬉皮士。如果他們是德克薩斯州人,他們可能不會在意消化兩倍容量的位元組。然而,加利福尼亞人無法忍受為字串消耗兩倍的儲存空間,並且不管怎樣,那已經有許多使用各種各樣ANSIDBCS位元組碼儲存的令人厭煩的文件,誰能來改變這一切?摩伊人(Moi)?出於這個原因,大部分人放棄Unicode若干年,事情又變得糟糕起來。

因此,發明了美妙的UTF-8規範。UTF-8是另一種儲存Unicode編碼的系統,那些神奇的U+數字,在記憶體中使用8位位元組。在UTF-8系統中,從0-127的每個字元碼用一個單位元組儲存。128以上的編碼,使用2個或者6,甚至6個位元組儲存。

這種方式的優雅之處在於,得英文文字的儲存在UTF-8ASCII系統中完全一樣,所以美國人沒有表示任何問題。只是世界上剩下的人們不得不適應這個轉變。特別的,HelloUnicode碼是:U+0048 U+0065 U+006C U+006C U+006F,將被儲存為:48 65 6C 6C 6F,看!跟在ASCII中、ANSI中,甚至這個星球上每個OEM字符集中一致。現在,如果你有勇氣去使用帶有重音的字母或是希臘字母或是克林貢字母,你將不得不若干位元組去儲存一個位元組碼,但是美國人永遠不會在意。(UTF-8還有一個很好的特性,就是舊的使用單獨的0作為空結束符的字串處理編碼不會截斷字串。)

到目前為止,我已經講解了三種Unicode

的編碼方式:傳統的2個位元組的儲存方式,UCS-2(因為他有兩個位元組)或是UTF-16(因為他有16位),同事你仍必須認出是否他是high-endianUCS-2還是low-endianUCS-2。還有流行的新的UTF-8標準,該標準可以在你需要考慮英語和那些完全沒有考慮ASCII以外編碼的腦殘程式的一致性方面,很好的工作。

事實上,還有很多Unicode的編碼方式。例如:UTF-7, 和UTF-8很類似,只是最高位永遠是0。所以,如果你需要給某些要求苛刻的,認為7位已經足夠的警察email系統傳遞Unicode,感謝你仍可以無損的壓縮位元組。還有UCS-4,用4個位元組儲存每個字元碼。好處就是每個字元都佔用同樣多的位元組,但是,天啊,德克薩斯人們又無法忍受浪費這麼多記憶體。

其實我們現在考慮的用Unicode展現的字元都是那些理想的字元,這些Unicode字元碼同樣也可以在任何的老派的編碼系統中編碼出來。例如:你可以給Hello進行Unicode編碼(U+0048 U+0065 U+006C U+006C U+006F),進行ASCII編碼或者古老的OEM希臘編碼,或者希伯來ANSI編碼,或者其他成百上千的編碼方式。但是有一點需要注意:有一些字元無法展現!如果在Unicode字元碼中沒有與之對應的編碼,例如,你常用遇到問號:?或者 � ,我改如何去做。

有成百上千的傳統的編碼僅僅可以儲存正確編碼,其餘的全部轉換成問號。一些流星的英語編碼方式是Winodws-1252(Windows9.x系列的西歐語言的編碼標準)和ISO-8859-1,又叫Latin-1(也是處理西歐語言的有效方式)。但是,如果嘗試用這些編碼儲存俄羅斯或者希伯來語,你將會得到一堆的問號。UTF7,8,1632都有很好特性,可以儲存任何正確的編碼。

編碼最重要的事實

如果你已經完全忘記了我前面闡述的東西,請記住一個重要的事實。如果他不知道你使用的編碼方式,那將無法工作。你不可能把你的頭埋在沙子裡,然後假裝說”普通”(plain)文字是ASCII的。

沒有所謂純文字的東西。

如果你有一段字串,不論在記憶體裡還是檔案裡或是在email中,你必須要知道他的編碼方式,否則你將無法解析和正確展示他。

幾乎所有的關於”我的網站看起來亂碼了”或是”當我在郵件裡用帶音調的字母的時候她就無法閱讀了”這樣愚蠢的問題,都是發生在那些不懂得上面關於編碼的簡單的事實的幼稚的程式設計師身上:如果你不知道字串是用UTF-8或是ASCII或是ISO8859-1(Latin 1)還是Windows1252(Western European)編碼,你就無法正確的展現它,甚至不知道他的結尾在哪裡。有超過一百的編碼和高127位的處理方式,一切猜測都沒有用。

那麼,我們怎麼儲存字串所使用的編碼型別的資訊呢?呵呵,這有一個標準的方式。例如對於郵件資訊,你希望在郵件頭裡看到類似這樣的一串字串:

Content-Type: text/plain; charset=”UTF-8”

對於網頁,原來的想法是web伺服器在web頁面本身返回一個類似的 Content-Type頭資訊,不是在HTML頁面本身,而是作為相應頭在HTML頁面之前返回。

這引發了一些問題。試想一下,你有一個大型的web伺服器,裡面有很多站點和非常多的頁面,為說各種語言的人們服務,並且頁面使用了適合他們的編碼來讀微軟的FrontPage頁面編碼。Web伺服器本身無法知道每個檔案編寫時的真正編碼,所以他無法傳送Content-Type頭資訊。

所以,如果你可以通過一些特殊的標籤,把Context-Type資訊放到HTML頁面本身那將非常方便。當然,這會使那些有純粹主義者瘋狂…

在你知道HTML檔案編碼之前,你怎樣去閱讀他?幸運的是,幾乎所有通常使用的編碼在32-127之前的處理方式都是一樣的。所以,你總可以在使用特殊符號之前,從HTML頁面中得到編碼資訊:

<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">

然而,這個元標籤必須是頭標記中的第一個元素,因為瀏覽器在看到這個標籤時,會立即停止原有的解析,而改為採用你指定的編碼進行解析。

如果瀏覽器在http頭和html標籤中都沒有找到Context-Type標記,會怎麼做呢?IE瀏覽器的做法比較有趣:他試圖基於各種位元組在不同語言的典型的文字、典型編碼中的出現頻率去猜測你的語言和編碼。因為,各種老的8位的編碼,習慣把他們本地的字母放在128-255之間不同的範圍內,同時因為每種人類語言都有自己的不同的字母使用典型直方圖,所以,這種猜測確實在某種程度中可行。這的確有寫些詭異,以至於使一個不知道需要在header中新增Content-Type資訊的天真web頁面的開發者,發現不了頁面的問題。直到有一天,他們開發了一些不完全符合字元頻率分部的本地語言的頁面,IE瀏覽器認為是韓語並且據此展現,這說明,Postel法案關於”保守的給予,自由的獲取”(OneCoder注:conservative in what you emit and liberal in what you accept)的觀點,對於工程理論來說是荒謬的。那麼,該網站可憐的讀者該怎麼辦?本來網站是用保加利亞語寫的,卻用韓語展現。(甚至還是無法理解的韓語)。如果他知道,他可以通過檢視->編碼選單嘗試不同的編碼,(那裡對於東歐的編碼就有一大堆),直到頁面開始顯示正常。當然,大部分人們並不知道去這麼做。

在我們公司釋出的網站管理軟體,CityDesk的最新版中,我們決定採用UCS-2來儲存Unicode,這正式VistualBasic,COM,和WindowsNT/2000/XP採用的表示本地字串的方式。在C++的程式碼中,我們用wchar_t(“wide char”)來宣告字串,而非使用char,並且使用wcs函式而非str函式。(例如:wcscatwcslen來代替strcatstrlen)。為了在C語言中宣告一個UCS-2的字串,你必須在前面加上一個L,變成:L”Hello”。

CityDesk釋出網頁版的時候,變成了這些年瀏覽器普遍支援良好的UTF-8編碼。這就是Joel on Software 網站29種不同語言版本的編碼方式,我還沒有聽說任何一個人有編碼問題。

這篇文章已經好長了。(OneCoder注:確實太長了,累吐血了。) 我不可能面面俱到的介紹關於字元編碼和 Unicode的一切,但是,我僅希望,如果你讀到了這裡,你可以擁有足夠知識去開發程式了。用抗生素治病,而不要用水蛭和符咒了,剩下的,就靠你自己了,孩子。

OneCoder注:頭一次翻譯,感覺翻譯的很生硬,錯誤也再所那面,望多多指教。</div>