前幾天看文初的《精武門(mén)之Web安全研討會(huì)首日感受》,說(shuō)到利用字符集攻擊時(shí)提到以前寶寶寫(xiě)的一篇有關(guān)國(guó)際化的文章,趁機(jī)再次拜讀了寶寶的這篇大作,不得不感慨寶寶的寫(xiě)作功底,無(wú)敵!這么好的文章不分享出來(lái)實(shí)在是太可惜了,在此將寶寶的大作轉(zhuǎn)帖于此; 作者序在我開(kāi)發(fā)Java程序的幾年中,遇到得最多,也是別人向我提問(wèn)最多的問(wèn)題,就是各種各樣看似稀奇古怪的中文亂碼問(wèn)題了。網(wǎng)上也有許多解釋和解決Java中文問(wèn)題的文章,但水平參差不齊,有一些文章甚至是錯(cuò)誤的。 此外,我們公司自己的Java程序從一開(kāi)始就采用了錯(cuò)誤的方式處理中文問(wèn)題,雖能解一時(shí)之急,卻引出了越來(lái)越多的深遠(yuǎn)的問(wèn)題。每當(dāng)我聽(tīng)到有的同事還在討論如何特殊處理雙字節(jié)的中文GB碼,就感慨他們思路的狹隘。試問(wèn),今天我們可以用特殊的方式處理我們所熟悉的中文編碼,可是今后我們?cè)鯓硬拍軕?yīng)付日文版、韓文版、或世界其它國(guó)家語(yǔ)言的產(chǎn)品開(kāi)發(fā)呢? 在我看來(lái),與其說(shuō)這些問(wèn)題是“中文化問(wèn)題”,不如說(shuō)是“國(guó)際化問(wèn)題”。所謂的“漢化”這種說(shuō)法已經(jīng)隨時(shí)代遠(yuǎn)去了。想想看,這個(gè)詞帶有明顯的小農(nóng)經(jīng)濟(jì)的色彩:自家漢化自家用,哪管世界變化多。經(jīng)過(guò)漢化的軟件,常常意味著:版本落后、不兼容、不穩(wěn)定。為什么會(huì)這樣呢?根本原因是,從軟件的設(shè)計(jì)階段,就沒(méi)有考慮國(guó)際用戶的需要,沒(méi)有采用國(guó)際通用的標(biāo)準(zhǔn)。事后要彌補(bǔ)自然難上加難。 所以讓我們把眼光放開(kāi),想一想“國(guó)際化”。當(dāng)然國(guó)際化的目的還是生產(chǎn)出“漢化”的軟件,但我們可以用同樣的方法“韓化”、“日化”、“阿拉伯化”,統(tǒng)稱為“本地化” —— 這就是“國(guó)際化”的目的。國(guó)際化和本地化有兩個(gè)很體面的英文縮寫(xiě):I18n(Internationalization)和L10n(Localization)。 想要開(kāi)發(fā)出國(guó)際化的軟件產(chǎn)品,首先要了解國(guó)際標(biāo)準(zhǔn),而不是使用東拼西湊的權(quán)宜之計(jì)。本文首先從相關(guān)國(guó)際標(biāo)準(zhǔn)的討論切入,相信正確地理解和應(yīng)用這些標(biāo)準(zhǔn),所有的“中文化問(wèn)題”或“國(guó)際化問(wèn)題”都會(huì)迎刃而解。 字符編碼簡(jiǎn)介ASCII碼從學(xué)計(jì)算機(jī)的那天開(kāi)始,老師就告訴我們?cè)谟?jì)算機(jī)里面,所有的英文字母都對(duì)應(yīng)到一個(gè)數(shù)字編碼,這就是ASCII碼(American Standard Code for Information Interchange)。ASCII碼是很久很久以前(1968年)制定的。它只使用了一個(gè)8位字節(jié)中的低7位,總共是127個(gè)編碼位。這樣的方案很快就不夠使用了。
單字節(jié)編碼的發(fā)展在80年代早期,一些現(xiàn)在流行的標(biāo)準(zhǔn)(如ISO 8859和Unicode)還未出現(xiàn)。那時(shí)為了支持多種地區(qū)的語(yǔ)言,各大組織機(jī)構(gòu)或IT廠商開(kāi)始發(fā)明它們自己的編碼方案,以便彌補(bǔ)ASCII編碼的不足。一時(shí)間,各種互不相容的字符編碼方案成百花齊放之勢(shì)。 為了避免混亂,ISO組織在1998年之后,陸續(xù)發(fā)表了一系列代號(hào)為8859的標(biāo)準(zhǔn),作為ASCII編碼的標(biāo)準(zhǔn)擴(kuò)展,終于統(tǒng)一了單字節(jié)的西方字符的編碼。ISO是設(shè)在瑞士的國(guó)際標(biāo)準(zhǔn)化組織的簡(jiǎn)稱(International Organization for Standardization)。 ISO-8859-1(Latin1 - 西歐字符) ISO-8859-1覆蓋了大多數(shù)西歐語(yǔ)言,包括:法國(guó)、西班牙、葡萄牙、意大利、荷蘭、德國(guó)、丹麥、瑞典、挪威、芬蘭、冰島、愛(ài)爾蘭、蘇格蘭、英格蘭等,因而也涉及到了整個(gè)美洲大陸、澳大利亞和非洲很多國(guó)家的語(yǔ)言。 此外,ISO-8859-1后來(lái)被采納為ISO-10646標(biāo)準(zhǔn)(后面會(huì)講到)的首頁(yè),換句話說(shuō),Unicode的最開(kāi)頭256個(gè)字符編碼和ISO-8859-1是一一對(duì)應(yīng)的。正是由于這個(gè)特殊性,使很多人產(chǎn)生了對(duì)ISO-8859-1編碼的誤用。
ISO-8859標(biāo)準(zhǔn)還包括:
但是ISO 8859系列標(biāo)準(zhǔn)的字符編碼,還是互不相容,不可能同時(shí)使用的。畢竟它們只是單字節(jié)的編碼方案。而且,它們和多字節(jié)的編碼方案如中文編碼GB2312和BIG5也是不相容的。那些歐洲字符(最高位為1的字符),在GB2312和BIG5中被認(rèn)為是雙字節(jié)漢字編碼的首字節(jié)。 多字節(jié)編碼的發(fā)展單字節(jié)編碼只有256個(gè)碼位(28=256),而中文字符何止千千萬(wàn),單字節(jié)編碼不可能滿足中文編碼的需要。于是為了適應(yīng)東方文字信息處理的需要,ISO又制定了ISO 2022標(biāo)準(zhǔn)(Character code structure and extension techniques),提供了七位與八位編碼字符集的擴(kuò)充方法的標(biāo)準(zhǔn)。我國(guó)根據(jù)ISO 2022制定了國(guó)家標(biāo)準(zhǔn)GB2311 ——《信息交換用七位編碼字符集的擴(kuò)充方法》,并根據(jù)該標(biāo)準(zhǔn)制定了國(guó)家標(biāo)準(zhǔn)GB2312-80編碼。其他東方國(guó)家和地區(qū)也制定了各自的字符編碼標(biāo)準(zhǔn),如日本的JIS0208,韓國(guó)的KSC5601,臺(tái)灣地區(qū)的CNS11643等。 BIG5 BIG5是從CNS11643的早期版本發(fā)展而來(lái)的,雖然沒(méi)有包括CNS11643的全部?jī)?nèi)容,但卻是目前臺(tái)灣、香港地區(qū)普遍使用的一種繁體漢字的市場(chǎng)標(biāo)準(zhǔn),包括440個(gè)符號(hào),一級(jí)漢字5401個(gè)、二級(jí)漢字7652個(gè),共計(jì)13060個(gè)漢字。 GB2312-80 全稱是《信息交換用漢字編碼字符集 基本集》,1980年發(fā)布,是中文信息處理的國(guó)家標(biāo)準(zhǔn),在大陸及海外使用簡(jiǎn)體中文的地區(qū)(如新加坡等)是強(qiáng)制使用的唯一中文編碼。 · 雙字節(jié)編碼 · A1-A9:符號(hào)區(qū),包含682個(gè)符號(hào) · B0-F7:漢字區(qū),包含6763個(gè)漢字 GB2312碼共收錄6763個(gè)簡(jiǎn)體漢字、682個(gè)符號(hào),其中漢字部分:一級(jí)字3755,以拼音排序,二級(jí)字3008,以偏旁排序。該標(biāo)準(zhǔn)的制定和應(yīng)用為規(guī)范、推動(dòng)中文信息化進(jìn)程起了很大作用。 GBK 漢字內(nèi)碼擴(kuò)展規(guī)范(GBK)是國(guó)家技術(shù)監(jiān)督局1995年為中文Windows 95所制定的新的漢字內(nèi)碼規(guī)范。 · 雙字節(jié)編碼,GB2312-80的擴(kuò)充,在碼位上和GB2312-80兼容。 · 范圍:8140 ~ FEFE(剔除xx7F)共23940個(gè)碼位。 · 包含21003個(gè)漢字,包含了ISO 10646中的全部中日韓漢字,簡(jiǎn)、繁體字融于一庫(kù)。 嚴(yán)格說(shuō),GBK不能算是國(guó)家標(biāo)準(zhǔn),最多算是一個(gè)商業(yè)標(biāo)準(zhǔn)。而GB18030才是真正的國(guó)家標(biāo)準(zhǔn)。 GB18030-2000 全稱是《信息交換用漢字編碼字符集》,是我國(guó)的強(qiáng)制標(biāo)準(zhǔn),所有不支持GB18030標(biāo)準(zhǔn)的軟件將不能作為產(chǎn)品出售。 · 單字節(jié)、雙字節(jié)、四字節(jié)編碼。 · 向下與GB2312編碼兼容。 · 支持GB 13000.1-1993中的全部中、日、韓(CJK)統(tǒng)一漢字字符和全部CJK統(tǒng)一漢字?jǐn)U展A的字符。 雖然GB18030標(biāo)準(zhǔn)非常強(qiáng)大,但它是一個(gè)中國(guó)大陸的標(biāo)準(zhǔn)。在編碼上,除了和GB2312以外,還是不能和世界上其它任何一種字符編碼統(tǒng)一。 終極標(biāo)準(zhǔn) —— Unicode和ISO 10646前面所講的一切字符編碼方案,都是針對(duì)局部地區(qū)或少數(shù)語(yǔ)言文字的,沒(méi)有辦法同時(shí)表達(dá)所有的語(yǔ)言文字,或在多種語(yǔ)言平臺(tái)上交換。這對(duì)今天極其頻繁的國(guó)際信息交流是不相稱的。 為了提高計(jì)算機(jī)的信息處理和交換功能,使得世界各國(guó)的文字都能在計(jì)算機(jī)中處理,從1984年起,ISO組織就開(kāi)始研究制定一個(gè)全新的標(biāo)準(zhǔn):通用多八位編碼字符集(Universal Multiple-Octet Coded Character Set),簡(jiǎn)稱UCS。標(biāo)準(zhǔn)的編號(hào)為:ISO 10646。這一標(biāo)準(zhǔn)為世界各種主要語(yǔ)言的字符(包括簡(jiǎn)體及繁體的中文字)及附加符號(hào),編制統(tǒng)一的內(nèi)碼。 統(tǒng)一碼(Unicode)是Universal Code的縮寫(xiě),是由另一個(gè)叫“Unicode學(xué)術(shù)學(xué)會(huì)”(The Unicode Consortium)的機(jī)構(gòu)制定的字符編碼系統(tǒng)。Unicode與ISO 10646國(guó)際編碼標(biāo)準(zhǔn)從內(nèi)容上來(lái)說(shuō)是同步一致的。 Unicode是Java語(yǔ)言和XML的基礎(chǔ),所以我們要稍微詳細(xì)地介紹一下Unicode以及ISO 10646標(biāo)準(zhǔn)。 注意:不夠耐心的讀者可以跳過(guò)本章的余下部分。但顯然了解本章所描述的Unicode及相關(guān)編碼的技術(shù)細(xì)節(jié),有利于你更好地理解和應(yīng)用Unicode。 Unicode和ISO 10646的關(guān)系在1991年,Unicode學(xué)術(shù)學(xué)會(huì)與ISO國(guó)際標(biāo)準(zhǔn)化組織決定共同制訂一套適用于多種語(yǔ)言文本的通用編碼標(biāo)準(zhǔn)。Unicode與ISO 10646國(guó)際編碼標(biāo)準(zhǔn)于1992年1月正式合作發(fā)展一套通用編碼標(biāo)準(zhǔn)。自此,兩個(gè)組織便一直緊密合作,同步發(fā)展Unicode及ISO 10646國(guó)際編碼標(biāo)準(zhǔn)。
雖然兩個(gè)組織保持如此密切的合作關(guān)系,但Unicode和ISO 10646還是有區(qū)別的。ISO 10646著重定義字符編碼,而Unicode則在此基礎(chǔ)上,為這些字符及編碼數(shù)據(jù)提出應(yīng)用的方法以及對(duì)語(yǔ)義數(shù)據(jù)作補(bǔ)充。 UCS的結(jié)構(gòu)UCS的結(jié)構(gòu)是一個(gè)四維的編碼空間,每一維由一個(gè)字節(jié)(八位二進(jìn)制位)組成,范圍是00到FF??傮w上分為128個(gè)群組(Group 00-7F),每一群組由256個(gè)平面(Plane 00-FF)組成,每一平面有256行(Row 00-FF),每一行256個(gè)編碼位(Cell 00-FF)。所以,每一平面包括65,536個(gè)字符位(Character Position 0000-FFFF)。 整個(gè)編碼字符集的每個(gè)字符都由4個(gè)字節(jié),按“組-面-行-列”的順序表示。所以UCS的可編碼空間為:128 × 256 × 256 × 256 = 231。 UCS將其第一個(gè)平面(00群組中的00平面)稱作基本多語(yǔ)種平面(Basic Multilingual Plane,BMP)。
在UCS中,目前只有00組是重要的,Unicode學(xué)術(shù)學(xué)會(huì)斷言,在可以預(yù)見(jiàn)的將來(lái),甚至不可能用完00組中的前17個(gè)平面(00平面到10平面)。因此,Unicode只定義了ISO 10646的第00組的前17個(gè)平面。事實(shí)上,目前絕大多數(shù)字符,都分配在第00平面BMP中。
下表中列出了BMP中的字符分配情況:
UCS的表現(xiàn)形式UCS有兩種方式來(lái)表示一個(gè)字符編碼:四字節(jié)正規(guī)形式(UCS-4,Four-octet canonical form)和雙字節(jié)基本平面形式(UCS-2,Two-octet BMP form)。 UCS-4 —— 四字節(jié)正規(guī)形式 UCS-4用4個(gè)字節(jié)來(lái)表示一個(gè)字符。第一個(gè)字節(jié)表示組(Group),第二表示平面(Plane),第三表示行(Row),第四表示單元號(hào)或列(Cell)。 UCS-2 —— 雙字節(jié)基本平面形式 當(dāng)系統(tǒng)只使用BMP的字符碼時(shí),可以省略群組和平面中的八位,將字符碼由32個(gè)位縮短為16個(gè)位(2個(gè)字節(jié))。標(biāo)記為UCS-2。 Unicode和UCS-2同樣采用16位編碼。所以一般可以把Unicode和UCS-2看作是同一樣?xùn)|西。 代理對(duì)(Surrogate Pair) UCS-4定義了4個(gè)字節(jié)表示一個(gè)字符,用來(lái)應(yīng)付將來(lái)的擴(kuò)展是綽綽有余。可是Unicode和UCS-2只定義了2個(gè)字節(jié),卻很容易用盡。代理對(duì)(Surrogate Pair)的設(shè)計(jì)在這種背景下應(yīng)運(yùn)而生。 UCS-2在BMP中開(kāi)辟了一個(gè)特殊的區(qū)間(D800 - DFFF) -- 代理區(qū),并平分成兩個(gè)區(qū),分別稱為高半代理區(qū)(High-half Zone,D800 - DBFF),和低半代理區(qū)(Low-half Zone,DC00 - DFFF),各有1024個(gè)碼位。使用時(shí),從高低兩個(gè)代理區(qū)中各取一個(gè)編碼組成一個(gè)四字節(jié)的代理,來(lái)表示一個(gè)在BMP以外平面上的編碼字符位。這樣一來(lái),總共可以多表示1024×1024個(gè)字符,映射到00群組中的01到10平面(共16個(gè)平面)。 代理對(duì)提供了用BMP的2字節(jié)編碼來(lái)表示在基本多文種平面(BMP)之外的16個(gè)平面編碼的機(jī)制。一些不常用的字符可以用代理對(duì)表示。目前,只有ISO/IEC 10646-2:2001和Unicode 3.1才使用到代理對(duì)。 高半代理區(qū)和低半代理區(qū)的劃分,使編碼位相互區(qū)分開(kāi)。非代理區(qū)字符一定不會(huì)在這個(gè)區(qū)里。因?yàn)楦甙氪韰^(qū)和低半代理區(qū)不相交,所以很容易決定字符值的邊界。一個(gè)完好的文本中,高半代理碼和低半代理碼總是按先后成對(duì)出現(xiàn)。 如果在實(shí)現(xiàn)上沒(méi)有刪除代理碼或在代理碼對(duì)中插入字符,數(shù)據(jù)的完整性就可得到保證。即使數(shù)據(jù)有殘損,也只是局部的。一個(gè)殘缺的碼只影響一個(gè)字符。因?yàn)楦甙氪韰^(qū)和低半代理區(qū)不相交,且成對(duì)出現(xiàn),錯(cuò)碼不會(huì)傳到文本的其它部分。 具體來(lái)說(shuō),一個(gè)代理對(duì)(H,L)由碼值為D800-DBFF 的高半代理碼H和碼值為 DC00-DFFF低半代理碼L組成。將一個(gè)字符映射到UCS-4碼位中。假設(shè)N是UCS-4碼值,則有:(以下所有數(shù)字均為16進(jìn)制) N = (H - D800) × 400 + (L - DC00) + 10000 于是得到N的碼值為10000到10FFFF。 注意 Unicode 3.0沒(méi)有用到代理對(duì),直到3.1才增加了CJK Ext B,用到了02平面,需要使用代理對(duì)才能訪問(wèn)。但99.99%的情況下,根本用不到那些字。此外,JDK1.4只支持到Unicode 3.0,所以目前Java還不能應(yīng)用代理對(duì)。 UTF編碼UTF為UCS Transformation Format的縮寫(xiě),意為“UCS轉(zhuǎn)換格式”。UCS只是一個(gè)字形和內(nèi)碼上的標(biāo)準(zhǔn),并沒(méi)有定義實(shí)際在計(jì)算機(jī)上存取的方法,而UTF便定義了一整套的計(jì)算機(jī)存取UCS編碼的轉(zhuǎn)換格式,并考慮了與其它編碼方式兼容。常用的格式有UTF-8和UTF-16。有時(shí)也用到UTF-7來(lái)進(jìn)行7位數(shù)據(jù)傳輸。 UTF-16 UTF-16是用定長(zhǎng)16位(2字節(jié))來(lái)表示的UCS-2或Unicode轉(zhuǎn)換格式。它將Unicode的編碼值變成2字節(jié)的Big-endian(高位字節(jié)在前,低位字節(jié)在后)或Little-endian(低位字節(jié)在前,高位字節(jié)在后)編碼。UTF-16利用代理對(duì)來(lái)訪問(wèn)BMP之外的字符編碼。 Java使用Big-endian系統(tǒng),而Intel系列處理器內(nèi)部使用Little-endian系統(tǒng)(學(xué)匯編語(yǔ)言和C語(yǔ)言的人都知道)。 例如:“中國(guó)”兩字,Unicode是4E2D 56FD,在Windows上用UTF-16編碼,結(jié)果為四個(gè)字節(jié):2D 4E FD 56;如果使用Java輸出,結(jié)果為:4E 2D 56 FD。 使用UTF-16有什么缺點(diǎn)呢?很顯然, 1. 所有原本1個(gè)字節(jié)就可以表示的西方字符,現(xiàn)在要用2個(gè)字節(jié)來(lái)表示,體積大了一倍。 2. 學(xué)過(guò)C的人都知道,0x00代表C字符串的結(jié)尾。但是用UTF-16來(lái)表示單字節(jié)字符(ISO-8859-1)時(shí),高位字節(jié)為0x00。這樣就會(huì)使C語(yǔ)言庫(kù)函數(shù)發(fā)生誤判。用UTF-16表示文件名、網(wǎng)址等,全引出無(wú)數(shù)的問(wèn)題。 3. 字符的邊界不好找。程序處理時(shí)必須從字符串的頭部開(kāi)始掃描,才可能正確地找出一個(gè)字符的邊界,效率較低。此外,萬(wàn)一壞掉一個(gè)字節(jié),這個(gè)字節(jié)之后的字符都會(huì)錯(cuò)位,壞掉一片。 所有的這些問(wèn)題,在UTF-8中都不存在。 但是,UTF-16也有其天然的優(yōu)點(diǎn):它直接表現(xiàn)了字符編碼的整數(shù)值。所以UTF-16是最直接的Unicode表示法。此外,它是定長(zhǎng)的,這大大簡(jiǎn)化了字符串的操作。Java語(yǔ)言就是用UTF-16格式將字符存儲(chǔ)在內(nèi)存中的。正是這樣,才使Java的Unicode字符串的操作格外簡(jiǎn)單高效。 UTF-8 UTF-8使用了變長(zhǎng)技術(shù),在每一個(gè)編碼區(qū)域有不同的字碼長(zhǎng)度: 1. 對(duì)UCS-2,由1字節(jié)至3字節(jié)構(gòu)成; 2. 如果UCS-2使用了代理對(duì),則UTF-8最長(zhǎng)可到4字節(jié); 3. 對(duì)UCS-4,由1字節(jié)至6字節(jié)構(gòu)成。 因?yàn)橐宰止?jié)(8位)為組成單元,故稱為“UTF-8”。對(duì)于英文文本,UTF-8的文件大小比其它轉(zhuǎn)換格式都小。 在UTF-8內(nèi),字符由1個(gè)至6個(gè)字節(jié)為組合。下表列舉出了不同范圍的UCS碼轉(zhuǎn)換成UTF-8的規(guī)則。英文字母“x”代表可以用來(lái)記錄 Unicode 碼值的區(qū)域。
在UTF-8內(nèi), 1. 如果一個(gè)字節(jié),最高位(第8位)為0,表示這是一個(gè)ASCII字符(00 - 7F)??梢?jiàn),所有ASCII編碼已經(jīng)是UTF-8了。 2. 如果一個(gè)字節(jié),以11開(kāi)頭,連續(xù)的1的個(gè)數(shù)暗示這個(gè)字符的字節(jié)數(shù),例如:110xxxxx代表它是雙字節(jié)UTF-8字符的首字節(jié)。 3. 如果一個(gè)字節(jié),以10開(kāi)始,表示它不是首字節(jié),需要向前查找才能得到當(dāng)前字符的首字節(jié)。 可見(jiàn)UTF-8可以有效地保證數(shù)據(jù)的完整性,避免出現(xiàn)編碼的錯(cuò)位。即使偶然出現(xiàn)“壞字”,也不會(huì)影響到后續(xù)的文本。 那么UTF-8有什么缺點(diǎn)呢?顯然,對(duì)于在BMP中的中文字來(lái)說(shuō),需要用3個(gè)字節(jié)才能表示,比使用UTF-16或直接使用雙字節(jié)的GB2312編碼大了0.5倍。
上文說(shuō)了一大通,總結(jié)一下,其實(shí)很簡(jiǎn)單:
|
|
來(lái)自: -ー意孤行ノ > 《中文化與國(guó)際化》