今天,用javascript腳本做了一個(gè)asp.net頁面中的菜單工具,保存為menuscript.js. 在頁面中用<script language="javascript" src="../js/menuscript.js"></script>調(diào)用, 結(jié)果在運(yùn)行中奇怪的現(xiàn)象發(fā)生了:頁面中的漢字正常顯示,可菜單中的漢字卻顯示為亂碼。
在1991年前后,雙方都認(rèn)識(shí)到世界不需要兩個(gè)不兼容的字符集。于是它們開始合并雙方的工作成果,并為創(chuàng)立一個(gè)單一編碼表而協(xié)同工作。從unicode2.0開始,unicode項(xiàng)目采用了與iso 10646-1相同的字庫和字碼。
目前兩個(gè)項(xiàng)目仍都存在,并獨(dú)立地公布各自的標(biāo)準(zhǔn)。unicode協(xié)會(huì)現(xiàn)在的最新版本是2005年的unicode 4.1.0。iso的最新標(biāo)準(zhǔn)是10646-3:2003。
ucs規(guī)定了怎么用多個(gè)字節(jié)表示各種文字。怎樣傳輸這些編碼,是由utf(ucs transformation format)規(guī)范規(guī)定的,常見的utf規(guī)范包括utf-8、utf-7、utf-16。
ietf的rfc2781和rfc3629以rfc的一貫風(fēng)格,清晰、明快又不失嚴(yán)謹(jǐn)?shù)孛枋隽藆tf-16和utf-8的編碼方法。我總是記不得ietf是internet engineering task force的縮寫。但ietf負(fù)責(zé)維護(hù)的rfc是internet上一切規(guī)范的基礎(chǔ)。
3、ucs-2、ucs-4、bmp
ucs有兩種格式:ucs-2和ucs-4。顧名思義,ucs-2就是用兩個(gè)字節(jié)編碼,ucs-4就是用4個(gè)字節(jié)(實(shí)際上只用了31位,最高位必須為0)編碼。下面讓我們做一些簡單的數(shù)學(xué)游戲:
ucs-2有2^16=65536個(gè)碼位,ucs-4有2^31=2147483648個(gè)碼位。
ucs-4根據(jù)最高位為0的最高字節(jié)分成2^7=128個(gè)group。每個(gè)group再根據(jù)次高字節(jié)分為256個(gè)plane。每個(gè)plane根據(jù)第3個(gè)字節(jié)分為256行 (rows),每行包含256個(gè)cells。當(dāng)然同一行的cells只是最后一個(gè)字節(jié)不同,其余都相同。
group 0的plane 0被稱作basic multilingual plane, 即bmp。或者說ucs-4中,高兩個(gè)字節(jié)為0的碼位被稱作bmp。
將ucs-4的bmp去掉前面的兩個(gè)零字節(jié)就得到了ucs-2。在ucs-2的兩個(gè)字節(jié)前加上兩個(gè)零字節(jié),就得到了ucs-4的bmp。而目前的ucs-4規(guī)范中還沒有任何字符被分配在bmp之外。
4、utf編碼
utf-8就是以8位為單元對(duì)ucs進(jìn)行編碼。從ucs-2到utf-8的編碼方式如下:
ucs-2編碼(16進(jìn)制) utf-8 字節(jié)流(二進(jìn)制)
0000 - 007f 0xxxxxxx
0080 - 07ff 110xxxxx 10xxxxxx
0800 - ffff 1110xxxx 10xxxxxx 10xxxxxx
例如“漢”字的unicode編碼是6c49。6c49在0800-ffff之間,所以肯定要用3字節(jié)模板了:1110xxxx 10xxxxxx 10xxxxxx。將6c49寫成二進(jìn)制是:0110 110001 001001, 用這個(gè)比特流依次代替模板中的x,得到:11100110 10110001 10001001,即e6 b1 89。
讀者可以用記事本測試一下我們的編碼是否正確。
utf-16以16位為單元對(duì)ucs進(jìn)行編碼。對(duì)于小于0x10000的ucs碼,utf-16編碼就等于ucs碼對(duì)應(yīng)的16位無符號(hào)整數(shù)。對(duì)于不小于0x10000的ucs碼,定義了一個(gè)算法。不過由于實(shí)際使用的ucs2,或者ucs4的bmp必然小于0x10000,所以就目前而言,可以認(rèn)為utf-16和ucs-2基本相同。但ucs-2只是一個(gè)編碼方案,utf-16卻要用于實(shí)際的傳輸,所以就不得不考慮字節(jié)序的問題。
5、utf的字節(jié)序和bom
utf-8以字節(jié)為編碼單元,沒有字節(jié)序的問題。utf-16以兩個(gè)字節(jié)為編碼單元,在解釋一個(gè)utf-16文本前,首先要弄清楚每個(gè)編碼單元的字節(jié)序。例如收到一個(gè)“奎”的unicode編碼是594e,“乙”的unicode編碼是4e59。如果我們收到utf-16字節(jié)流“594e”,那么這是“奎”還是“乙”?
unicode規(guī)范中推薦的標(biāo)記字節(jié)順序的方法是bom。bom不是“bill of material”的bom表,而是byte order mark。bom是一個(gè)有點(diǎn)小聰明的想法:
在ucs編碼中有一個(gè)叫做"zero width no-break space"的字符,它的編碼是feff。而fffe在ucs中是不存在的字符,所以不應(yīng)該出現(xiàn)在實(shí)際傳輸中。ucs規(guī)范建議我們在傳輸字節(jié)流前,先傳輸字符"zero width no-break space"。
這樣如果接收者收到feff,就表明這個(gè)字節(jié)流是big-endian的;如果收到fffe,就表明這個(gè)字節(jié)流是little-endian的。因此字符"zero width no-break space"又被稱作bom。
utf-8不需要bom來表明字節(jié)順序,但可以用bom來表明編碼方式。字符"zero width no-break space"的utf-8編碼是ef bb bf(讀者可以用我們前面介紹的編碼方法驗(yàn)證一下)。所以如果接收者收到以ef bb bf開頭的字節(jié)流,就知道這是utf-8編碼了。
windows就是使用bom來標(biāo)記文本文件的編碼方式的。
6、進(jìn)一步的參考資料
本文主要參考的資料是 "short overview of iso-iec 10646 and unicode" (http://www.nada.kth.se/i18n/ucs/unicode-iso10646-oview.html)。
我還找了兩篇看上去不錯(cuò)的資料,不過因?yàn)槲议_始的疑問都找到了答案,所以就沒有看:
"understanding unicode a general introduction to the unicode standard" (http://scripts.sil.org/cms/scripts/page.php?site_id=nrsi&item_id=iws-chapter04a)
"character set encoding basics understanding character set encodings and legacy encodings" (http://scripts.sil.org/cms/scripts/page.php?site_id=nrsi&item_id=iws-chapter03)
我寫過utf-8、ucs-2、gbk相互轉(zhuǎn)換的軟件包,包括使用windows api和不使用windows api的版本。以后有時(shí)間的話,我會(huì)整理一下放到我的個(gè)人主頁上(http://fmddlmyy.home4u.china.com)。
我是想清楚所有問題后才開始寫這篇文章的,原以為一會(huì)兒就能寫好。沒想到考慮措辭和查證細(xì)節(jié)花費(fèi)了很長時(shí)間,竟然從下午1:30寫到9:00。希望有讀者能從中受益。
新聞熱點(diǎn)
疑難解答
圖片精選