編碼

要存儲(chǔ),編碼的概念當(dāng)然就被引入進(jìn)來。

Unicode最早的編碼想法,就是把每一個(gè)碼點(diǎn)(code point)都存儲(chǔ)在兩個(gè)字節(jié)中,這也就導(dǎo)致了大多數(shù)人的誤解。于是Hello就變成了:

00 48 00 65 00 6C 00 6C 00 6F

這樣對嗎?如下如何?

48 00 65 00 6C 00 6C 00 6F 00

技術(shù)上說,我相信這樣是可以的。事實(shí)上,早期的實(shí)現(xiàn)者們的確想把Unicode的碼點(diǎn)(code point)按照大端或小端兩種方式存儲(chǔ),這樣至少已經(jīng)有兩種存儲(chǔ)Unicode的方法了。于是人們就必須使用FE FF作為每一個(gè)Unicode字符串的開頭,我們稱這個(gè)為Unicode Byte Order Mark。如果你互換了你的高位與低位,就變成了FF FE,這樣讀取這個(gè)字符串的程序就知道后面字節(jié)也需要互換了??上В皇敲恳粋€(gè)Unicode字符串都有字節(jié)序標(biāo)記。

現(xiàn)在,看起來好像問題已經(jīng)解決了,可是這幫程序員仍在抱怨。"看看這些零!"他們會(huì)這樣說,因?yàn)樗麄兪敲绹耍麄冎豢床粫?huì)碼點(diǎn)不會(huì)超過U+00FF的英文字母。同時(shí)他們也是California的嬉皮士,他們想節(jié)省一點(diǎn)。如果他們是得克薩斯人,可能他們就不會(huì)介意兩倍的字節(jié)數(shù)。但是這樣California節(jié)儉的人卻無法忍受字符串所占空間翻倍。而且現(xiàn)在大堆的文檔使用的是ANSI和DBCS字符集,誰去轉(zhuǎn)換它們?于是這幫人選擇忽略Unicode,繼續(xù)自己的路,這顯然讓事情變得更糟。

于是非常聰明的UTF-8的概念被引入了。UTF-8是另一個(gè)系統(tǒng),用來存儲(chǔ)字符串所對應(yīng)的Unicode的碼點(diǎn) (code points)-即那些神奇的U+數(shù)字組合,在內(nèi)存中,而且存儲(chǔ)的最小單元是8比特的字節(jié)。在UTF-8中,0-127之間的碼字都使用一個(gè)字節(jié)來存儲(chǔ),超過128的碼字使用2,3甚至6個(gè)字節(jié)來存儲(chǔ)。

關(guān)于字符集和Unicode的相關(guān)知識(shí)

這顯然有非常好的效果,因?yàn)橛⑽牡奈谋臼褂肬TF-8存儲(chǔ)的形式完全與ASCII一樣了,所以美國人壓根不會(huì)注意到發(fā)生了什么變化。舉個(gè)例子,Hello -- U+0048 U+0065 U+006C U+006C U+006C U+006F,將會(huì)被存儲(chǔ)為48 65 6C 6C 6F,這與ASCII、與ANSI標(biāo)準(zhǔn)、與所有這個(gè)星球上的OEM字符集顯然都是一樣的?,F(xiàn)在,如果你需要使用希臘字母,你可以用幾個(gè)字節(jié)來存儲(chǔ)一個(gè)碼字,美國人永遠(yuǎn)都不會(huì)注意到。(干嗎得美國人注意,無語,美國人寫的文章...)

到現(xiàn)在我已經(jīng)告訴了你三種Unicode的編碼方式,傳統(tǒng)的使用兩個(gè)字節(jié)存儲(chǔ)的稱之為UCS-2或者UTF-16,而且你必須判斷空間是大端的UCS-2還是小端的UCS-2。新的UTF-8標(biāo)準(zhǔn)顯然更流行,如果你恰巧有專門面向英文的程序,顯然這些程序不需要知道UTF-8的存在依然可以工作地很好。

事實(shí)上,還有其它若干對Unicode編碼的方法。比如有個(gè)叫UTF-7,和UTF-8差不多,但是保證字節(jié)的最高位總是0,這樣如果你的字符不得不經(jīng)過一些嚴(yán)格的郵件系統(tǒng)時(shí)(這些系統(tǒng)認(rèn)為7個(gè)比特完全夠用了),就不會(huì)有信息丟失。還有一個(gè)UCS-4,使用4個(gè)字節(jié)來存儲(chǔ)每個(gè)碼點(diǎn)(code point),好處是每個(gè)碼點(diǎn)都使用相同的字節(jié)數(shù)來存儲(chǔ),可惜這次就算是得克薩斯人也不愿意了,因?yàn)檫@個(gè)方法實(shí)在太浪費(fèi)了。

現(xiàn)在的情況變成了你思考事情時(shí)所使用的基本單元--柏拉圖式的字母已經(jīng)被Unicode的碼點(diǎn)完全表示了。這些碼點(diǎn)也可以完全使用其它舊的編碼體系。比如,你可以把 Hello對應(yīng)的Unicode碼點(diǎn)串(U+0048 U+0065 U+006C U+006C U+006F)用ASCII、OEM Greek、Hebrew ANSI或其它上百個(gè)編碼體系來編碼,不過需要注意一點(diǎn),有些字母會(huì)無法顯示。如果你要表示的Unicode碼點(diǎn)在你使用的編碼體系中壓根沒有對應(yīng)的字符,那么你可能會(huì)得到一個(gè)小問號(hào)"?",或者得到一個(gè)"�"。

許多傳統(tǒng)的編碼體系僅僅能編碼Unicode碼點(diǎn)中的一部分,其余全部會(huì)被顯示為問號(hào)。比較流行的英文編碼體系有Windows-1252(Windows 9x中的西歐語言標(biāo)準(zhǔn))和ISO-8859-1,還有aka Latin-1。但是如果想用這些編碼體系來編碼俄語或者希伯來語就只能得到一串問號(hào)了。UTF 7,8,16,和32都可以完全正確編碼Unicode中的所有碼點(diǎn)。

關(guān)于編碼的唯一事實(shí)

如果你完全忘掉了我剛剛解釋過的內(nèi)容,沒有關(guān)系,請記住一點(diǎn),如果你不知道一個(gè)字符串所使用的編碼,這個(gè)字符串在你手中也就毫無意義。你不能再把腦袋埋進(jìn)沙中以為"純文本"就是ASCII。事實(shí)上,

根本就不存在所謂的"純文本"。

那么我們?nèi)绾蔚弥粋€(gè)字符串所使用的空間是何種編碼呢?對于這個(gè)問題已經(jīng)有了標(biāo)準(zhǔn)的作法。如果是一份電子郵件,你必須在格式的頭部有如下語句:

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

對于一個(gè)網(wǎng)頁,傳統(tǒng)的想法是Web服務(wù)器會(huì)返回一個(gè)類似于Content-Type的http頭和Web網(wǎng)頁,注意,這里的字符編碼并不是在HTML中指出,而是在獨(dú)立的響應(yīng)headers中指出。

這帶來了一些問題。假設(shè)你擁有一個(gè)大的Web服務(wù)器,擁有非常多的站點(diǎn),每個(gè)站點(diǎn)都包括數(shù)以百計(jì)的Web頁面,而寫這些頁面的人可能使用不同的語言,他們在他們自己計(jì)算機(jī)上的FrontPage等工具中看到頁面正常顯示就提交了上來,顯然,服務(wù)器是沒有辦法知道這些文件究竟使用的是何種編碼,當(dāng)然 Content-Type頭也沒有辦法發(fā)送了。

如果可以把Content-Type夾在HTML文件中,那不是會(huì)變得非常方便?這個(gè)想法會(huì)讓純粹論者發(fā)瘋,你如何在不知道它的編碼的情況下讀一個(gè)HTML文件呢?答案很簡單,因?yàn)閹缀跛械木幋a在32-127的碼字都做相同的事情,所以不需要使用特殊字符,你可以從HTML文件中獲得你想要的Content-Type。

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

注意,這里的meta標(biāo)簽必須在head部分第一個(gè)出現(xiàn),一旦瀏覽器看到這個(gè)標(biāo)簽就會(huì)馬上停止解析頁面,然后使用這個(gè)標(biāo)簽中給出的編碼從頭開始重新解析整個(gè)頁面。

如果瀏覽器在http頭或者meta標(biāo)簽中都找不到相關(guān)的Content-Type信息,那應(yīng)該怎么辦?Internet Explorer做了一些事情:它試圖猜測出正確的編碼,基于不同語言編碼中典型文本中出現(xiàn)的那些字節(jié)的頗率。因?yàn)楣爬系?比特的碼頁(code pages)傾向于把它們的國家編碼放置在128-255碼字的范圍內(nèi),而不同的人類語言字母系統(tǒng)中的字母使用頗率對應(yīng)的直方圖會(huì)有不同,所以這個(gè)方法可以奏效。雖然很怪異,但對于那些老忘記寫Content-Type的幼稚網(wǎng)頁編寫者而言,這個(gè)方法大多數(shù)情況下可以讓他們的頁面顯然OK。直到有一天,他們寫的頁面不再滿足"letter-frequency-distribution",Internet Explore覺得這應(yīng)該是朝鮮語,于是就當(dāng)朝鮮語來顯示了,結(jié)果顯然糟透了。這個(gè)頁面的讀者們立刻就遭殃了,一個(gè)保加利亞語寫的頁面卻用朝鮮語來顯示,效果會(huì)怎樣?于是讀者使用 查看-->編碼 菜單來不停地試啊試,直到他終于試出了正確的編碼,但前提是他知道可以這樣做,事實(shí)上大多數(shù)人根本不會(huì)這樣做。

在我的公司開發(fā)的一款Web頁面管理軟件CityDesk的最新版本中,我們決定像Visual Basic、COM和Windows NT/2000/XP所做的那樣,整個(gè)過程中使用UCS-2(兩個(gè)字節(jié))Unicode。在我們寫的C++代碼中,我們把所有的char類型換成了wchar_t,所有使用str函數(shù)的地方,換成了相應(yīng)的wcs函數(shù)(如使用wcscatwcslen來替代strcatstrlen)。如果想在C中創(chuàng)建一個(gè)UCS-2的字符串,只需在字符串前面加L即可:L"Hello"。

當(dāng)CityDesk發(fā)布頁面的時(shí)候,它把所有的頁面都轉(zhuǎn)換成了UTF-8編碼,而差不多所有的瀏覽器都對UTF-8有不錯(cuò)的支持。這就是"Joel On Software"(就是作者的首頁)編碼的方式,所以即使它擁有29個(gè)語言版本,至今也未聽到有一個(gè)人抱怨頁面無法瀏覽。

這篇文章已經(jīng)有點(diǎn)長了,而且我也沒有辦法告訴你關(guān)于字符編碼和Unicode的所有應(yīng)該了解的知識(shí),但讀到現(xiàn)在我想你已經(jīng)掌握到基本的概念,回去編程時(shí)可以使用抗生素而不是螞蝗和咒語了,這就看做是留給你的作業(yè)吧。

標(biāo)簽:Unicode

相關(guān)文章

隨機(jī)推薦