1 編碼
在計(jì)算機(jī)中,各種信息都是以二進(jìn)制編碼的形式存在的,也就是說(shuō),不管是文字、圖形、聲音、動(dòng)畫(huà),還是電影等各種信息,在計(jì)算機(jī)中都是以0和1組成的二進(jìn)制代碼表示的。為了區(qū)分這些信息,人們就為計(jì)算機(jī)設(shè)計(jì)了一套識(shí)別準(zhǔn)則,這也就是計(jì)算機(jī)編碼。例如:英文字母與漢字的的區(qū)別,就是英文字母用的是單字節(jié)的ASCII碼,漢字采用的是雙字節(jié)的漢字內(nèi)碼。
1.1 基本概念
* 字符:字符是抽象的最小文本單位。它沒(méi)有固定的形狀(可能是一個(gè)字形),而且沒(méi)有值。“A”是一個(gè)字符,“”(德國(guó)、法國(guó)和許多其他歐洲國(guó)家通用貨幣的標(biāo)志)也是一個(gè)字符。
* 字符集:字符集是字符的集合。例如,漢字字符是中國(guó)人最先發(fā)明的字符,在中文、日文、韓文和越南文的書(shū)寫(xiě)中使用。
* 編碼字符集:編碼字符集是一個(gè)字符集,它為每一個(gè)字符分配一個(gè)唯一數(shù)字。Unicode 標(biāo)準(zhǔn)的核心是一個(gè)編碼字符集,字母“A”的編碼為 004116 和字符“”的編碼為 20AC16。Unicode 標(biāo)準(zhǔn)始終使用十六進(jìn)制數(shù)字,而且在書(shū)寫(xiě)時(shí)在前面加上前綴“U+”,所以“A”的編碼書(shū)寫(xiě)為“U+0041”。
* 代碼點(diǎn):代碼點(diǎn)是指可用于編碼字符集的數(shù)字。編碼字符集定義一個(gè)有效的代碼點(diǎn)范圍,但是并不一定將字符分配給所有這些代碼點(diǎn)。有效的Unicode代碼點(diǎn)范圍是 U+0000 至U+10FFFF。Unicode4.0將字符分配給一百多萬(wàn)個(gè)代碼點(diǎn)中的96,382代碼點(diǎn)。
* 增補(bǔ)字符:增補(bǔ)字符是代碼點(diǎn)在 U+10000 至 U+10FFFF 范圍之間的字符,也就是那些使用原始的Unicode的16 位設(shè)計(jì)無(wú)法表示的字符。從U+0000至 U+FFFF之間的字符集有時(shí)候被稱為基本多語(yǔ)言面 (BMP)。因此,每一個(gè)Unicode 字符要么屬于BMP,要么屬于增補(bǔ)字符。
* 字符編碼方案:字符編碼方案是從一個(gè)或多個(gè)編碼字符集到一個(gè)或多個(gè)固定寬度代碼單元序列的映射。最常用的代碼單元是字節(jié),但是 16 位或 32 位整數(shù)也可用于內(nèi)部處理。UTF-32、UTF-16 和 UTF-8 是 Unicode 標(biāo)準(zhǔn)的編碼字符集的字符編碼方案。
* UTF-32:即將每一個(gè) Unicode 代碼點(diǎn)表示為相同值的 32 位整數(shù)。很明顯,它是內(nèi)部處理最方便的表達(dá)方式,但是,如果作為一般字符串表達(dá)方式,則要消耗更多的內(nèi)存。
* UTF-16:使用一個(gè)或兩個(gè)未分配的 16 位代碼單元的序列對(duì)Unicode代碼點(diǎn)進(jìn)行編碼。值U+0000至U+FFFF編碼為一個(gè)相同值的16位單元。增補(bǔ)字符編碼為兩個(gè)代碼單元,第一個(gè)單元來(lái)自于高代理范圍(U+D800 至 U+DBFF),第二個(gè)單元來(lái)自于低代理范圍(U+DC00至U+DFFF)。這在概念上可能看起來(lái)類似于多字節(jié)編碼,但是其中有一個(gè)重要區(qū)別:值 U+D800至U+DFFF 保留用于UTF-16;沒(méi)有這些值分配字符作為代碼點(diǎn)。這意味著,對(duì)于一個(gè)字符串中的每個(gè)單獨(dú)的代碼單元,軟件可以識(shí)別是否該代碼單元表示某個(gè)單單元字符,或者是否該代碼單元是某個(gè)雙單元字符的第一個(gè)或第二單元。這相當(dāng)于某些傳統(tǒng)的多字節(jié)字符編碼來(lái)說(shuō)是一個(gè)顯著的改進(jìn),在傳統(tǒng)的多字節(jié)字符編碼中,字節(jié)值 0x41既可能表示字母“A”,也可能是一個(gè)雙字節(jié)字符的第二個(gè)字節(jié)。
* UTF-8:使用一至四個(gè)字節(jié)的序列對(duì)編碼Unicode代碼點(diǎn)進(jìn)行編碼。U+0000至U+007F 使用一個(gè)字節(jié)編碼,U+0080至U+07FF使用兩個(gè)字節(jié),U+0800至U+FFFF 使用三個(gè)字節(jié),而U+10000至U+10FFFF使用四個(gè)字節(jié)。UTF-8設(shè)計(jì)原理為:字節(jié)值0x00至0x7F 始終表示代碼點(diǎn)U+0000至U+007F(Basic Latin 字符子集,它對(duì)應(yīng) ASCII 字符集)。這些字節(jié)值永遠(yuǎn)不會(huì)表示其他代碼點(diǎn),這一特性使UTF-8可以很方便地在軟件中將特殊的含義賦予某些ASCII字符。
(表1-1 編碼的一些基本概念)
1.2 字符編碼
字符編碼即英文編碼,包括字母、數(shù)字、標(biāo)點(diǎn)、運(yùn)算符等。
字符的編碼采用國(guó)際通用的ASCII碼(American Standard Code for Information Interchange,美國(guó)信息交換標(biāo)準(zhǔn)代碼),每個(gè)ASCII碼以1個(gè)字節(jié)(Byte)儲(chǔ)存,從0到數(shù)字127代表不同的常用符號(hào),例如大寫(xiě)A的 ASCII碼是65,小寫(xiě)a則是97。由于ASCII碼只用了字節(jié)的七個(gè)位,最高位并不使用,所以后來(lái)又將最高的一個(gè)位也編入這套編碼碼中,成為八個(gè)位的延伸ASCII(ExtendedASCII)碼,這套內(nèi)碼加上了許多外文和表格等特殊符號(hào),成為目前常用的編碼。基本的ASCII字符集共有128個(gè)字符,其中有96個(gè)可打印字符,包括常用的字母、數(shù)字、標(biāo)點(diǎn)符號(hào)等,另外還有32個(gè)控制字符。標(biāo)準(zhǔn)ASCII碼使用7個(gè)二進(jìn)位對(duì)字符進(jìn)行編碼,對(duì)應(yīng)的ISO 標(biāo)準(zhǔn)為ISO646標(biāo)準(zhǔn)。
字母和數(shù)字的ASCII碼的記憶是非常簡(jiǎn)單的。我們只要記住了一個(gè)字母或數(shù)字的ASCII碼(例如記住A為65,0的ASCII碼為48),知道相應(yīng)的大小寫(xiě)字母之間差32,就可以推算出其余字母、數(shù)字的ASCII碼。
雖然標(biāo)準(zhǔn)ASCII碼是7位編碼,但由于計(jì)算機(jī)基本處理單位為字節(jié)(1byte = 8bit),所以一般仍以一個(gè)字節(jié)來(lái)存放一個(gè)ASCII字符。每一個(gè)字節(jié)中多余出來(lái)的一位(最高位)在計(jì)算機(jī)內(nèi)部通常保持為0(在數(shù)據(jù)傳輸時(shí)可用作奇偶校驗(yàn)位)。由于標(biāo)準(zhǔn)ASCII字符集字符數(shù)目有限,在實(shí)際應(yīng)用中往往無(wú)法滿足要求。為此,國(guó)際標(biāo)準(zhǔn)化組織又制定了ISO2022標(biāo)準(zhǔn),它規(guī)定了在保持與 ISO646兼容的前提下將ASCII字符集擴(kuò)充為8位代碼的統(tǒng)一方法。ISO陸續(xù)制定了一批適用于不同地區(qū)的擴(kuò)充ASCII字符集,每種擴(kuò)充ASCII 字符集分別可以擴(kuò)充128個(gè)字符,這些擴(kuò)充字符的編碼均為高位為1的8位代碼(即十進(jìn)制數(shù)128~255),稱為擴(kuò)展ASCII碼。
1.3 漢字編碼
1.3.1 漢字內(nèi)碼
漢字信息在計(jì)算機(jī)內(nèi)部也是以二進(jìn)制方式存放。由于漢字?jǐn)?shù)量多,用一個(gè)字節(jié)的128種狀態(tài)不能全部表示出來(lái),因此在1980年我國(guó)頒布的《信息交換用漢字編碼字符集——基本集》,即國(guó)家標(biāo)準(zhǔn)GB2312-80方案中規(guī)定用兩個(gè)字節(jié)的十六位二進(jìn)制表示一個(gè)漢字,每個(gè)字節(jié)都只使用低7位(與ASCII碼相同),即有128×128=16384種狀態(tài)。由于ASCII碼的34個(gè)控制代碼在漢字系統(tǒng)中也要使用,為不致發(fā)生沖突,不能作為漢字編碼,128除去34只剩 94種,所以漢字編碼表的大小是94×94=8836,用以表示國(guó)標(biāo)碼規(guī)定的7445個(gè)漢字和圖形符號(hào)。
每個(gè)漢字或圖形符號(hào)分別用兩位的十進(jìn)制區(qū)碼(行碼)和兩位的十進(jìn)制位碼(列碼)表示,不足的地方補(bǔ)0,組合起來(lái)就是區(qū)位碼。把區(qū)位碼按一定的規(guī)則轉(zhuǎn)換成的二進(jìn)制代碼叫做信息交換碼(簡(jiǎn)稱國(guó)標(biāo)碼)。國(guó)標(biāo)碼共有漢字6763個(gè)(一級(jí)漢字,是最常用的漢字,按漢語(yǔ)拼音字母順序排列,共3755個(gè);二級(jí)漢字,屬于次常用漢字,按偏旁部首的筆劃順序排列,共3008個(gè)),數(shù)字、字母、符號(hào)等682個(gè),共7445個(gè)。
由于國(guó)標(biāo)碼不能直接存儲(chǔ)在計(jì)算機(jī)內(nèi),為方便計(jì)算機(jī)內(nèi)部處理和存儲(chǔ)漢字,又區(qū)別于ASCII碼,將國(guó)標(biāo)碼中的每個(gè)字節(jié)在最高位改設(shè)為1,這樣就形成了在計(jì)算機(jī)內(nèi)部用來(lái)進(jìn)行漢字的存儲(chǔ)、運(yùn)算的編碼叫機(jī)內(nèi)碼(或漢字內(nèi)碼,或內(nèi)碼)。內(nèi)碼既與國(guó)標(biāo)碼有簡(jiǎn)單的對(duì)應(yīng)關(guān)系,易于轉(zhuǎn)換,又與ASCII碼有明顯的區(qū)別,且有統(tǒng)一的標(biāo)準(zhǔn)(內(nèi)碼是惟一的)。
1.3.2 漢字外碼
無(wú)論是區(qū)位碼或國(guó)標(biāo)碼都不利于輸入漢字,為方便漢字的輸入而制定的漢字編碼,稱為漢字輸入碼,即漢字外碼。不同的輸入方法,形成了不同的漢字外碼。常見(jiàn)的輸入法有以下幾類:
* 按漢字的排列順序形成的編碼(流水碼):如區(qū)位碼;
* 按漢字的讀音形成的編碼(音碼):如全拼、簡(jiǎn)拼、雙拼等;
* 按漢字的字形形成的編碼(形碼):如五筆字型、鄭碼等;
* 按漢字的音、形結(jié)合形成的編碼(音形碼):如自然碼、智能ABC。
* 輸入碼在計(jì)算機(jī)中必須轉(zhuǎn)換成機(jī)內(nèi)碼,才能進(jìn)行存儲(chǔ)和處理。
(表1-2 常見(jiàn)的輸入法)
1.3.3 漢字字形碼
為了將漢字在顯示器或打印機(jī)上輸出,把漢字按圖形符號(hào)設(shè)計(jì)成點(diǎn)陣圖,就得到了相應(yīng)的點(diǎn)陣代碼(字形碼)。
全部漢字字碼的集合叫漢字字庫(kù)。漢字庫(kù)可分為軟字庫(kù)和硬字庫(kù)。軟字庫(kù)以文件的形式存放在硬盤(pán)上,現(xiàn)多用這種方式,硬字庫(kù)則將字庫(kù)固化在一個(gè)單獨(dú)的存儲(chǔ)芯片中,再和其它必要的器件組成接口卡,插接在計(jì)算機(jī)上,通常稱為漢卡。
用于顯示的字庫(kù)叫顯示字庫(kù)。顯示一個(gè)漢字一般采用16×16點(diǎn)陣或24×24點(diǎn)陣或48×48點(diǎn)陣。已知漢字點(diǎn)陣的大小,可以計(jì)算出存儲(chǔ)一個(gè)漢字所需占用的字節(jié)空間。例:用16×16點(diǎn)陣表示一個(gè)漢字,就是將每個(gè)漢字用16行,每行16個(gè)點(diǎn)表示,一個(gè)點(diǎn)需要1位二進(jìn)制代碼,16個(gè)點(diǎn)需用16位二進(jìn)制代碼(即2個(gè)字節(jié)),共16行,所以需要16行×2字節(jié)/行=32字節(jié),即16×16點(diǎn)陣表示一個(gè)漢字,字形碼需用32字節(jié)。即:字節(jié)數(shù) = 點(diǎn)陣行數(shù) * 點(diǎn)陣列數(shù) / 8。
用于打印的字庫(kù)叫打印字庫(kù),其中的漢字比顯示字庫(kù)多,而且工作時(shí)也不像顯示字庫(kù)需調(diào)入內(nèi)存。可以這樣理解,為在計(jì)算機(jī)內(nèi)表示漢字而統(tǒng)一的編碼方式形成漢字編碼叫內(nèi)碼(如國(guó)標(biāo)碼),內(nèi)碼是惟一的。為方便漢字輸入而形成的漢字編碼為輸入碼,屬于漢字的外碼,輸入碼因編碼方式不同而不同,是多種多樣的。為顯示和打印輸出漢字而形成的漢字編碼為字形碼,計(jì)算機(jī)通過(guò)漢字內(nèi)碼在字模庫(kù)中找出漢字的字形碼,實(shí)現(xiàn)其轉(zhuǎn)換。例如:
1:已知漢字"春"的國(guó)標(biāo)碼為343AH,求其機(jī)內(nèi)碼?
答:機(jī)內(nèi)碼 = 國(guó)標(biāo)碼 + 8080H = 343AH + 8080H = B4BAH
2:用24×24點(diǎn)陣來(lái)表示一個(gè)漢字(一點(diǎn)為一個(gè)二進(jìn)制位),則2000個(gè)漢字需要多少KB容量?
答: 容量 = (24 * 24/8)* 2000 / 1024 = 140.7KB ≈ 141KB
(表1-3 漢字內(nèi)碼與字形碼的轉(zhuǎn)換實(shí)例)
2 UNICODE編碼
Unicode 是基于通用字符集(Universal Character Set)的標(biāo)準(zhǔn)來(lái)發(fā)展,并且同時(shí)也以書(shū)本的形式(The Unicode Standard,目前第五版由Addison-Wesley Professional出版,ISBN-10: 0321480910)對(duì)外發(fā)表。Unicode包含了超過(guò)十萬(wàn)個(gè)字符(在2005年,Unicode的第十萬(wàn)個(gè)字符被采納且認(rèn)可成為標(biāo)準(zhǔn)之一)、一組可用以作為視覺(jué)參考的代碼圖表、一套編碼方法與一組標(biāo)準(zhǔn)字符編碼、一套包含了上標(biāo)字、下標(biāo)字等字符特性的列舉等。
在計(jì)算機(jī)科學(xué)領(lǐng)域中,Unicode(統(tǒng)一碼、萬(wàn)國(guó)碼、單一碼、標(biāo)準(zhǔn)萬(wàn)國(guó)碼)是業(yè)界的一種標(biāo)準(zhǔn),它可以使電腦得以呈現(xiàn)世界上數(shù)十種文字的系統(tǒng)。Unicode組織(The Unicode Consortium)是由一個(gè)非營(yíng)利性的機(jī)構(gòu)所運(yùn)作,并主導(dǎo)Unicode的后續(xù)發(fā)展,其目標(biāo)在于:將既有的字符編碼方案,以Unicode編碼方案來(lái)加以取代,特別是既有的方案在多語(yǔ)環(huán)境下,皆僅有有限的空間以及不相容的問(wèn)題。
Unicode在字符集認(rèn)可的成功,使其得以在電腦軟件的國(guó)際化與本地化領(lǐng)域中,廣泛且具優(yōu)勢(shì)的被采用。這標(biāo)準(zhǔn)已在近年來(lái)的多種新科技當(dāng)中被加以采用,包含了可擴(kuò)展置標(biāo)語(yǔ)言(XML)、Java編程語(yǔ)言、以及最新的操作系統(tǒng)中。
2.2 UNICODE的起源與發(fā)展
Unicode是由于傳統(tǒng)的字符編碼方式的局限性而產(chǎn)生的,例如 ISO 8859所定義的字符雖然在不同的國(guó)家中廣泛地使用,可是在不同國(guó)家間卻經(jīng)常出現(xiàn)不相容的情況。很多傳統(tǒng)的編碼方式都具有一個(gè)共通的問(wèn)題,即其容許電腦進(jìn)行雙語(yǔ)環(huán)境式的處理(通常使用拉丁字母以及其本地語(yǔ)言),但卻無(wú)法同時(shí)支援多語(yǔ)言環(huán)境式的處理(指可同時(shí)處理混合多種語(yǔ)言的情況)。
Unicode試圖將字位(字素,graphemes)與類字位字符加以認(rèn)定與編碼,而非以不同的字形(glyphs)來(lái)加以區(qū)分。然而在漢字的個(gè)案來(lái)看,這樣方式有時(shí)會(huì)引起一字多形的認(rèn)定爭(zhēng)議(詳見(jiàn)中日韓統(tǒng)一表意文字主題)。
在文字處理方面,Unicode的功用是為每一個(gè)字符提供一個(gè)唯一的代碼(即一組數(shù)字),而不是一種字形。換句話說(shuō),Unicode是將字符以一種抽象的方式來(lái)呈現(xiàn),而將視覺(jué)上的演繹工作(例如字體大小、外觀形狀、字體形態(tài)、文體等)留給其他軟件來(lái)處理,例如網(wǎng)頁(yè)瀏覽器或是文字處理器。
為了使Unicode與已存在和廣泛使用的舊有編碼互相兼容,尤其是差不多所有電腦系統(tǒng)都支援的基本拉丁字母部分,所以Unicode的首256字符仍舊保留給ISO 8859-1所定義的字符,使既有的西歐語(yǔ)系文字的轉(zhuǎn)換不需特別考量;另方面因相同的原因,Unicode 把大量相同的字符重復(fù)編到不同的字符碼中去,使得舊有紛雜的編碼方式得以和 Unicode 編碼間互相直接轉(zhuǎn)換,而不會(huì)遺失任何資訊。舉例來(lái)說(shuō),全角格式區(qū)段包含了主要的拉丁字母的全角格式,在中文、日文、以及韓文字形當(dāng)中,這些字符以全角的方式來(lái)呈現(xiàn),而不以常見(jiàn)的半角形式顯示,這對(duì)豎排文字和等寬排列文字有重要作用。
Unicode組織位于美國(guó)加州,組織允許任何愿意支付會(huì)員費(fèi)用的公司或是個(gè)人加入,其成員包含了主要的電腦軟硬件廠商,例如奧多比系統(tǒng)(Adobe Systems)、蘋(píng)果公司(Apple)、惠普(HP)、IBM、微軟(Microsoft)、全錄(Xerox)等。Unicode 組織在 1991 年首次發(fā)布了 The Unicode Standard(ISBN 0-321-18578-1)。Unicode的開(kāi)發(fā)結(jié)合了國(guó)際標(biāo)準(zhǔn)化組織(International Organization for Standardization,簡(jiǎn)稱 ISO)所制定的ISO/IEC 10646,即通用字符集(Universal Character Set,簡(jiǎn)稱 UCS)。Unicode 與 ISO/IEC 10646 在編碼的運(yùn)作原理相同,但 The Unicode Standard 包含了更詳盡的實(shí)現(xiàn)資訊、涵蓋了更細(xì)節(jié)的主題,諸如字符編碼(bitwise encoding)、校對(duì)以及呈現(xiàn)等。 The Unicode Standard 也列舉了諸多的字符特性,包含了那些必須支援雙方向呈現(xiàn)的文字(由左至右或由右至左的文字呈現(xiàn)方向,例如阿拉伯文是由右至左)。Unicode與 ISO/IEC 10646兩個(gè)標(biāo)準(zhǔn)在術(shù)語(yǔ)上的使用有些微的不同。
Unicode截至目前為止歷次的版次與發(fā)布時(shí)間如下:
Unicode 1.0:1991年10月;
Unicode 1.0.1:1992年6月;
Unicode 1.1:1993年6月;
Unicode 2.0:1997年7月;
Unicode 2.1:1998年5月;
Unicode 2.1.2:1998年5月;
Unicode 3.0:1999年9月;涵蓋了來(lái)自ISO 10646-1的十六位元通用字符集(UCS)基本多文種平面(Basic Multilingual Plane);
Unicode 3.1:2001年3月;新增從ISO 10646-2定義的輔助平面(Supplementary Planes);
Unicode 3.2:2002年3月;
Unicode 4.0:2003年4月;
Unicode 4.0.1:2004年3月;
Unicode 4.1:2005年3月;
Unicode 5.0:2006年7月;
Unicode 5.1:2008年4月。
(表2-1 unicode編碼的版次與發(fā)布時(shí)間)
2.3 Unicode 的編碼和實(shí)現(xiàn)
2.3.1 編碼方式
Unicode 的編碼方式與 ISO 10646 的通用字符集(Universal Character Set,UCS)概念相對(duì)應(yīng),目前實(shí)際應(yīng)用的 Unicode 版本對(duì)應(yīng)于 UCS-2,使用16位的編碼空間。也就是每個(gè)字符占用2個(gè)字節(jié)。這樣理論上一共最多可以表示 216 即 65536 個(gè)字符。基本滿足各種語(yǔ)言的使用。實(shí)際上目前版本的 Unicode 尚未填充滿這16位編碼,保留了大量空間作為特殊使用或?qū)?lái)擴(kuò)展。
上述16位Unicode字符構(gòu)成基本多文種平面(Basic Multilingual Plane,簡(jiǎn)稱BMP)。最新(但未實(shí)際廣泛使用)的Unicode 版本定義了16個(gè)輔助平面,兩者合起來(lái)至少需要占據(jù)21位的編碼空間,比3字節(jié)略少。但事實(shí)上輔助平面字符仍然占用4字節(jié)編碼空間,與UCS-4保持一致。未來(lái)版本會(huì)擴(kuò)充到 ISO 10646-1 實(shí)現(xiàn)級(jí)別3,即涵蓋UCS-4的所有字符。UCS-4 是一個(gè)更大的尚未填充完全的31位字符集,加上恒為0的首位,共需占據(jù)32位,即4字節(jié)。理論上最多能表示231個(gè)字符,完全可以涵蓋一切語(yǔ)言所用的符號(hào)。
BMP字符的Unicode編碼表示為 U+hhhh,其中每個(gè) h 代表一個(gè)十六進(jìn)制數(shù)位。與UCS-2編碼完全相同。對(duì)應(yīng)的4字節(jié)UCS-4編碼后兩個(gè)字節(jié)一致,前兩個(gè)字節(jié)的所有位均為0。
2.3.2 實(shí)現(xiàn)方式
Unicode的實(shí)現(xiàn)方式不同于編碼方式。一個(gè)字符的Unicode 編碼是確定的。但是在實(shí)際傳輸過(guò)程中,由于不同系統(tǒng)平臺(tái)的設(shè)計(jì)不一定一致,以及出于節(jié)省空間的目的,對(duì)Unicode編碼的實(shí)現(xiàn)方式有所不同。 Unicode的實(shí)現(xiàn)方式稱為Unicode轉(zhuǎn)換格式(Unicode Translation Format,簡(jiǎn)稱為 UTF)。
例如,如果一個(gè)僅包含基本7位ASCII字符的Unicode文件,如果每個(gè)字符都使用2字節(jié)的原Unicode 編碼傳輸,其第一字節(jié)的8位始終為0。這就造成了比較大的浪費(fèi)。對(duì)于這種情況,可以使用 UTF-8 編碼,這是一種變長(zhǎng)編碼,它將基本7位ASCII字符仍用7位編碼表示,占用一個(gè)字節(jié)(首位補(bǔ)0)。而遇到與其他 Unicode 字符混合的情況,將按一定算法轉(zhuǎn)換,每個(gè)字符使用1-3個(gè)字節(jié)編碼,并利用首位為0或1進(jìn)行識(shí)別。這樣對(duì)以7位ASCII字符為主的西文文檔就大大節(jié)省了編碼長(zhǎng)度。類似的,對(duì)未來(lái)會(huì)出現(xiàn)的需要4個(gè)字節(jié)的輔助平面字符和其他 UCS-4 擴(kuò)充字符,2字節(jié)編碼的UTF-16也需要通過(guò)一定的算法進(jìn)行轉(zhuǎn)換。
再如,如果直接使用與Unicode編碼一致(僅限于 BMP 字符)的UTF-16編碼,由于每個(gè)字符占用了兩個(gè)字節(jié),在Macintosh機(jī)和PC機(jī)上,對(duì)字節(jié)順序的理解是不一致的。這時(shí)同一字節(jié)流可能會(huì)被解釋為不同內(nèi)容,如編碼為U+594E的字符“奎”同編碼為 U+4E59 的“乙”就可能發(fā)生混淆。于是在UTF-16編碼實(shí)現(xiàn)方式中使用了大尾序(big-endian)、小尾序(little-endian)的概念,以及 BOM(Byte Order Mark)解決方案。
此外 Unicode 的實(shí)現(xiàn)方式還包括UTF-7、Punycode、CESU-8、SCSU、UTF-32等,這些實(shí)現(xiàn)方式有些僅在一定的國(guó)家和地區(qū)使用,有些則屬于未來(lái)的規(guī)劃方式。目前通用的實(shí)現(xiàn)方式是 UTF-16小尾序(BOM)、UTF-16大尾序(BOM)和 UTF-8。在微軟公司W(wǎng)indows XP操作系統(tǒng)附帶的記事本中,“另存為”對(duì)話框可以選擇的四種編碼方式除去非 Unicode 編碼的 ANSI 外,其余三種“Unicode”、“Unicode big endian”和“UTF-8”即分別對(duì)應(yīng)這三種實(shí)現(xiàn)方式。
3 UTF-8
UTF是UCS / Unicode Transformation Format(Unicode轉(zhuǎn)換格式)的縮寫(xiě),UTF-8(8位元Universal Character Set/Unicode Transformation Format)是一種針對(duì) Unicode 的可變長(zhǎng)度字符編碼。它可以用來(lái)表示 Unicode 標(biāo)準(zhǔn)中的任何字符,且其編碼中的第一個(gè)字節(jié)仍與ASCII相容,這使得原來(lái)處理ASCII字符的軟件無(wú)須或只須做少部份修改,即可繼續(xù)使用。因此,它逐漸成為電子郵件、網(wǎng)頁(yè)及其他儲(chǔ)存或傳送文字的應(yīng)用中,優(yōu)先采用的編碼。
3.1 UTF-8的歷史
1992年初,為建立良好的字節(jié)串編碼系統(tǒng)(byte-stream encoding)以供多字節(jié)字符集(multi-byte character sets)使用,開(kāi)始了一個(gè)正式的研究。ISO/IEC 10646的初稿中有一個(gè)非必須的附錄,名為UTF。當(dāng)中包含了一個(gè)供32位元的字符使用的字節(jié)串編碼系統(tǒng)。這個(gè)編碼方式的性能并不令人滿意,但它提出了將0-127的范圍保留給ASCII以相容舊系統(tǒng)的概念。
1992年7月,X/Open委員會(huì)XoJIG開(kāi)始尋求一個(gè)較佳的編碼系統(tǒng)。Unix系統(tǒng)實(shí)驗(yàn)室(UNIX System Laboratories, USL)的Dave Prosser為此提出了一個(gè)編碼系統(tǒng)的建議。它具備可更快速實(shí)作的特性,并引入一項(xiàng)新的改進(jìn)。其中,7位元的ASCII符號(hào)只代表原來(lái)的意思,所有多字節(jié)序列則會(huì)包含第8位元的符號(hào),也就是所謂的最高有效位元(Most significant bit)。
1992年8月,這個(gè)建議由IBMX/Open的代表流傳到一些感興趣的團(tuán)體。與此同時(shí),貝爾實(shí)驗(yàn)室Plan 9操作系統(tǒng)工作小組的肯·湯普遜對(duì)這編碼系統(tǒng)作出重大的修改,讓編碼可以自我同步(self-synchronizing),使得不必從字串的開(kāi)首讀取,也能找出字符間的分界。1992年9月2日,肯·湯普遜和Rob Pike一起在美國(guó)新澤西州一架餐車的餐桌墊上描繪出此設(shè)計(jì)的要點(diǎn)。接下來(lái)的日子,Pike及湯普遜將它實(shí)現(xiàn),并將這編碼系統(tǒng)完全應(yīng)用在Plan 9當(dāng)中,及后他將有關(guān)成果回饋X/Open。
1993年1月25-29日的在圣地牙哥舉行的USENIX會(huì)議首次正式介紹UTF-8。
自1996年起,微軟的CAB(MS Cabinet)規(guī)格在UTF-8標(biāo)準(zhǔn)正式落實(shí)前就明確容許在任何地方使用UTF-8編碼系統(tǒng)。但有關(guān)的編碼器實(shí)際上從來(lái)沒(méi)有實(shí)作這方面的規(guī)格。
3.2 UTF-8的優(yōu)缺點(diǎn)
3.2.1 UTF-8的特質(zhì)
UTF-8的設(shè)計(jì)有以下的多字符組序列的特質(zhì):
?單字節(jié)字符的最高有效位元永遠(yuǎn)為0;
?多字節(jié)序列中的首個(gè)字符組的幾個(gè)最高有效位元決定了序列的長(zhǎng)度。最高有效位為110的,是2字節(jié)序列,而1110的是三字節(jié)序列,如此類推;
?多字節(jié)序列中其余的字節(jié)中的首兩個(gè)最高有效位元為10。
(表3-1 UTF-8的特質(zhì))
UTF-8的這些特質(zhì),保證了一個(gè)字符的字節(jié)序列不會(huì)包含在另一個(gè)字符的字節(jié)序列中。這確保了以字節(jié)為基礎(chǔ)的部份字串比對(duì)(sub-string match)方法可以適用于在文字中搜尋字或詞。有些比較舊的可變長(zhǎng)度8位元編碼(如Shift JIS)沒(méi)有這個(gè)特質(zhì),故字串比對(duì)的算法變得相當(dāng)復(fù)雜。雖然這增加了UTF-8編碼的字串的信息冗余,但是利多于弊。另外,資料壓縮并非Unicode的目的,所以不可混為一談。即使在傳送過(guò)程中有部份字節(jié)因錯(cuò)誤或干擾而完全遺失,還是有可能在下一個(gè)字符的起點(diǎn)重新同步,令受損范圍受到限制。
另一方面,由于其字節(jié)序列設(shè)計(jì),如果一個(gè)疑似為字符串的序列被驗(yàn)證為UTF-8編碼,那么我們可以有把握地說(shuō)它是UTF-8字符串。一段兩字節(jié)隨機(jī)序列碰巧為合法的UTF-8而非ASCII 的機(jī)率為32分1。對(duì)于三字節(jié)序列的機(jī)率為256分3,對(duì)更長(zhǎng)的序列的機(jī)率就更低了。
3.2.2 UTF-8的優(yōu)點(diǎn)
UTF-8編碼可以通過(guò)屏蔽位和移位操作快速讀寫(xiě)。字符串比較時(shí) strcmp()和wcscmp()的返回結(jié)果相同,因此使排序變得更加容易。字節(jié)FF和FE在UTF-8編碼中永遠(yuǎn)不會(huì)出現(xiàn),因此他們可以用來(lái)表明 UTF-16或UTF-32文本(見(jiàn)BOM) UTF-8 是字節(jié)順序無(wú)關(guān)的。它的字節(jié)順序在所有系統(tǒng)中都是一樣的,因此它實(shí)際上并不需要BOM。
UTF-8是ASCII的一個(gè)超集。因?yàn)橐粋€(gè)純ASCII字符串也是一個(gè)合法的UTF-8字符串,所以現(xiàn)存的ASCII文本不需要轉(zhuǎn)換。為傳統(tǒng)的擴(kuò)展ASCII字符集設(shè)計(jì)的軟件通常可以不經(jīng)修改或很少修改就能與UTF-8 一起使用;
使用標(biāo)準(zhǔn)的面向字節(jié)的排序例程對(duì)UTF-8排序?qū)a(chǎn)生與基于 Unicode代碼點(diǎn)排序相同的結(jié)果。(盡管這只有有限的有用性,因?yàn)樵谌魏翁囟ㄕZ(yǔ)言或文化下都不太可能有仍可接受的文字排列順序);
UTF-8和UTF-16都是可擴(kuò)展標(biāo)記語(yǔ)言文檔的標(biāo)準(zhǔn)編碼,所有其它編碼都必須通過(guò)顯式或文本聲明來(lái)指定;
任何面向字節(jié)的字符串搜索算法都可以用于UTF-8的數(shù)據(jù)(只要輸入僅由完整的UTF-8字符組成)。但是,對(duì)于包含字符記數(shù)的正則表達(dá)式或其它結(jié)構(gòu)必須小心。
UTF-8字符串可以由一個(gè)簡(jiǎn)單的算法可靠地識(shí)別出來(lái)。就是:一個(gè)字符串在任何其它編碼中表現(xiàn)為合法的UTF-8的可能性很低,并隨字符串長(zhǎng)度增長(zhǎng)而減小。舉例說(shuō),字符值C0,C1,F5至FF從來(lái)沒(méi)有出現(xiàn)。為了更好的可靠性,可以使用正則表達(dá)式來(lái)統(tǒng)計(jì)非法過(guò)長(zhǎng)和替代值(可以查看W3 FAQ: Multilingual Forms上的驗(yàn)證UTF-8字符串的正則表達(dá)式)。
3.2.3 UTF-8的缺點(diǎn)
A 不利于正則表達(dá)式檢索,正則表達(dá)式可以進(jìn)行很多英文高級(jí)的模糊檢索。例如,[a-h]表示a到h間所有字母。同樣GBK編碼的中文也可以這樣利用正則表達(dá)式,比如在只知道一個(gè)字的讀音而不知道怎么寫(xiě)的情況下,也可用正則表達(dá)式檢索,因?yàn)镚BK編碼是按讀音排序的。只是UTF-8不是按讀音排序的,所以會(huì)對(duì)正則表達(dá)式檢索造成不利影響。但是這種使用方式并未考慮中文中的破音字,因此影響不大。Unicode是按部首排序的,因此在只知道一個(gè)字的部首而不知道如何發(fā)音的情況下,UTF-8 可用正則表達(dá)式檢索而GBK不行。
B 你無(wú)法從UNICODE字符數(shù)判斷出 UTF-8文本的字節(jié)數(shù),因?yàn)閁TF-8是一種變長(zhǎng)編碼它需要用2個(gè)字節(jié)編碼那些用擴(kuò)展ASCII字符集只需1個(gè)字節(jié)的字符 ISO Latin-1 是UNICODE的子集,但不是UTF-8的子集 8位字符的UTF-8編碼會(huì)被email網(wǎng)關(guān)過(guò)濾,因?yàn)閕nternet信息最初設(shè)計(jì)為7為ASCII碼。因此產(chǎn)生了UTF-7編碼。UTF-8 在它的表示中使用值100xxxxx的幾率超過(guò)50%,而現(xiàn)存的實(shí)現(xiàn)如ISO 2022,4873,6429,和8859系統(tǒng),會(huì)把它錯(cuò)認(rèn)為是C1 控制碼。因此產(chǎn)生了UTF-7.5編碼。
一份寫(xiě)得很差(并且與當(dāng)前標(biāo)準(zhǔn)的版本不兼容)的UTF-8解析器可能會(huì)接受一些不同的偽UTF-8表示并將它們轉(zhuǎn)換到相同的Unicode輸出上。這為設(shè)計(jì)用于處理八位表示的校驗(yàn)例程提供了一種遺漏信息的方式。
C 占據(jù)空間大,與其他 Unicode 編碼相比,特別是UTF-16,在 UTF-8 中 ASCII 字符占用的空間只有一半,可是在一些字符的 UTF-8 編碼占用的空間就要多出,特別是中文、日文和韓文(CJK)這樣的象形文字,所以具體因素因文檔而異,但不論哪種情況,差別都不可能很明顯。
3.3 UTF-8的編碼方式
UTF-8是UNICODE的一種變長(zhǎng)度的編碼表達(dá)方式(一般 UNICODE為雙字節(jié)[指UCS2]),UTF-8就是以8位為單元對(duì)UCS進(jìn)行編碼,而UTF-8不使用大尾序和小尾序的形式,每個(gè)使用UTF-8儲(chǔ)存的字符,除了第一個(gè)字節(jié)外,其余字節(jié)的頭兩個(gè)位元都是以"10"開(kāi)始,使文字處理器能夠較快地找出每個(gè)字符的開(kāi)始位置。
為了與以前的ASCII碼相容(ASCII為一個(gè)字節(jié)),因此 UTF-8 選擇了使用可變長(zhǎng)度字節(jié)來(lái)儲(chǔ)存 Unicode,具體轉(zhuǎn)換關(guān)系如下表:
UCS-4(UNICODE)編碼
UTF-8字節(jié)流
U-00000000 – U-0000007F
0xxxxxxx
U-00000080 – U-000007FF
110xxxxx 10xxxxxx
U-00000800 – U-0000FFFF
1110xxxx 10xxxxxx 10xxxxxx
U-00010000 – U-001FFFFF
11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
U-00200000 – U-03FFFFFF
111110xx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx
U-04000000 – U-7FFFFFFF
1111110x 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx
(表3-2 Unicode和UTF-8之間的轉(zhuǎn)換關(guān)系表)
在ASCII碼的范圍,用一個(gè)字節(jié)表示,超出ASCII碼的范圍就用字節(jié)表示,這就形成了我們上面看到的UTF-8的表示方法,這?的好處是當(dāng)UNICODE文件中只有ASCII碼時(shí),儲(chǔ)存的文件都為一個(gè)字節(jié),所以就是普通的ASCII文件無(wú)異,讀取的時(shí)候也是如此,所以能與以前的ASCII文件相容。
大于ASCII碼的,就會(huì)由上面的第一字節(jié)的前幾位表示該unicode字符的長(zhǎng)度,比如110xxxxxx前三位的二進(jìn)制表示告訴我們這是個(gè) 2BYTE的UNICODE字符;1110xxxx是個(gè)三位的UNICODE字符,依此類推;xxx 的位置由字符編碼數(shù)的二進(jìn)制表示的位填入。越靠右的 x 具有越少的特殊意義。只用最短的那個(gè)足夠表達(dá)一個(gè)字符編碼數(shù)的多字節(jié)串。注意在多字節(jié)串中,第一個(gè)字節(jié)的開(kāi)頭"1"的數(shù)目就是整個(gè)串中字節(jié)的數(shù)目。
ASCII字母繼續(xù)使用1字節(jié)儲(chǔ)存,重音文字、希臘字母或西里爾字母等使用2字節(jié)來(lái)儲(chǔ)存,而常用的漢字就要使用3字節(jié)。輔助平面字符則使用4字節(jié)。
在UTF-8文件的開(kāi)首,很多時(shí)都放置一個(gè)U+FEFF字符(UTF-8以EF,BB,BF代表),以顯示這個(gè)文字檔案是以UTF-8編碼。
4 UNICODE與UTF-8的轉(zhuǎn)換
4.1 UNICODE轉(zhuǎn)換為UTF-8
UTF-8的特點(diǎn)是對(duì)不同范圍的字符使用不同長(zhǎng)度的編碼。對(duì)于 0x00-0x7F之間的字符,UTF-8編碼與ASCII編碼完全相同。UTF-8編碼的最大長(zhǎng)度是4個(gè)字節(jié)。從表3-2可以看出,4字節(jié)模板有21個(gè) x,即可以容納21位二進(jìn)制數(shù)字。Unicode的最大碼位0x10FFFF也只有21位。
如:“漢”字的Unicode編碼是0x6C49。0x6C49在0x0800-0xFFFF之間,使用用3字節(jié)模板了:1110xxxx 10xxxxxx 10xxxxxx。將0x6C49寫(xiě)成二進(jìn)制是:0110 1100 0100 1001, 用這個(gè)比特流依次代替模板中的x,得到:11100110 10110001 10001001,即E6 B1 89。
又如:Unicode編碼0x20C30在0x010000-0x10FFFF之間,使用4字節(jié)模板了:11110xxx 10xxxxxx 10xxxxxx 10xxxxxx。將0x20C30寫(xiě)成21位二進(jìn)制數(shù)字(不足21位就在前面補(bǔ)0):0 0010 0000 1100 0011 0000,用這個(gè)比特流依次代替模板中的x,得到:11110000 10100000 10110000 10110000,即F0 A0 B0 B0。
4.2 UTF-8轉(zhuǎn)換為UNICODE
4.2.1 java中文亂碼出現(xiàn)的原因
亂碼指的是計(jì)算機(jī)系統(tǒng)不能顯示正確的字符,而顯示其他無(wú)意義的字符或空白,如一堆ASCII代碼。這樣所顯示出來(lái)的文本統(tǒng)稱為亂碼。
亂碼是因?yàn)?#8220;所使用的字符的源碼在本地計(jì)算機(jī)上使用了錯(cuò)誤的顯示字庫(kù)”,或在本地計(jì)算機(jī)的字庫(kù)中找不到相應(yīng)于源碼所指代的字符所致。不同國(guó)家和地區(qū)的文本字庫(kù)采用了相同的一段源碼,或是源文件中因?yàn)槲募艿狡茐模率褂?jì)算機(jī)默認(rèn)提取的源碼錯(cuò)誤,或是計(jì)算機(jī)沒(méi)有安裝相應(yīng)字庫(kù),都有可能產(chǎn)生亂碼。
java 采用unicode 編碼來(lái)處理字符。Java 程序無(wú)論是從向文件系統(tǒng)以字符流讀、寫(xiě)文件,還是向URL連接寫(xiě)HTML信息,或從URL連接讀取參數(shù)值,都會(huì)有字符編碼的轉(zhuǎn)換。下圖編碼——解碼的示意圖:
(圖4-1 編碼/解碼示意圖)
java亂碼產(chǎn)生的根源是由于編碼和解碼采用的不是同一種碼(GBK、 UTF-8、iso8859-1、GB2312等),如將UNICODE編碼按照UTF-8進(jìn)行了編碼,而解碼的時(shí)候卻用的是iso8859-1,此時(shí)在 java程序中便會(huì)出現(xiàn)與原UNICODE編碼不一致的情況,即出現(xiàn)亂碼。下面以字符串“中國(guó)”為例,具體說(shuō)明中文亂碼出現(xiàn)的原因。
字符(String或char[])"中國(guó)" 經(jīng)過(guò)java 編碼后的字節(jié)流(unicode 字節(jié)流)為:4E 2D 56 FD,如果你用new String("中國(guó)".getBytes("UTF-8"), "GB2312") 就會(huì)產(chǎn)生亂碼,如圖:
(圖4-2 亂碼出現(xiàn)原因示意圖)
因?yàn)間etBytes("UTF-8") 取得的是"中國(guó)" 經(jīng)過(guò)UTF-8編碼后的字節(jié)流E4 B8 AD E5 9B BD(UTF-8字節(jié)流),而在用new String(bytes, "GB2312") 構(gòu)造字符串時(shí)java 則將UTF-8字節(jié)流(E4 B8 AD E5 9B BD)當(dāng)作是unicode 字節(jié)流(因?yàn)閖ava是采用unicode 來(lái)處理字符的,所以它把字節(jié)流統(tǒng)統(tǒng)當(dāng)作是unicode 字節(jié)流),因此它把E4 B8 AD E5 9B BD也看成是unicode字節(jié)流。而unicode 字節(jié)流(E4 B8 AD E5 9B BD)經(jīng)過(guò)GB2312 編碼后就成了“涓??”。于是,亂碼產(chǎn)生了。
解決亂碼的一般方法是進(jìn)行正確的解碼操作,如編碼使用的是utf-8 的方式,那么解碼是也須用utf-8進(jìn)行解碼。
正確的解碼流程應(yīng)該是:
(圖4-3 UTF-8正確的解碼示意圖)
4.2.2 將UTF-8轉(zhuǎn)換為UNICODE
圖3-3勾畫(huà)了將UTF-8轉(zhuǎn)換為UNICODE的基本流程,根據(jù)表3-2(Unicode和UTF-8之間的轉(zhuǎn)換關(guān)系)可以看出,將UTF-8轉(zhuǎn)換為 UNICODE的過(guò)程,實(shí)際上是將按UTF-8編碼規(guī)則填充至UNICODE的編碼提取出來(lái)的過(guò)程,即將8位的字節(jié)“xxxx xxxx”提取出來(lái)的過(guò)程,如某字符以“1110xxxx 10xxxxxx 10xxxxxx”的編碼方式進(jìn)行UTF-8編碼,還原后UNICODE編碼為:“xxxx xxxx”。
例如字符“中”,二進(jìn)制序列(雙字節(jié))為:0100 1110 0010 1101,進(jìn)行UTF-8編碼后的字節(jié)二進(jìn)制序列(三字節(jié))為:1110 0100 10 111000 10 101101,進(jìn)行UTF-8轉(zhuǎn)碼即去掉第一個(gè)字節(jié)的“1110”,第二個(gè)字節(jié)的“10”,第三個(gè)字節(jié)的“10”,然后再將剩余的二進(jìn)制序列組合成一個(gè)雙字節(jié)的二進(jìn)制序列,即還原為:0100 1110 0010 1101,如此則完成轉(zhuǎn)碼。
下面的代碼是將以“1110xxxx 10xxxxxx 10xxxxxx”的編碼方式進(jìn)行UTF-8編碼,還原為UNICODE編碼的操作:
/**
* 將utf-8編碼轉(zhuǎn)化為unicode編碼
* @param aByte byte[] -原utf-8編碼字節(jié)數(shù)組
* return sByte byte[] -轉(zhuǎn)化后的unicode編碼字節(jié)數(shù)組
*/
public static String changeUtf8ToUnicode(byte[] aByte) {
int sLength = aByte.length; //原字節(jié)數(shù)組長(zhǎng)度
//存儲(chǔ)轉(zhuǎn)化為unicode編碼后的StringBuffer字符串
StringBuffer sUnicodeStringBuffer = new StringBuffer();
char sChar; //用于臨時(shí)存放每個(gè)從utf-8中解析出來(lái)的unicode編碼
//以下操作是判斷字節(jié)是否以"1110 xxxx 10xxxxxx 10xxxxxx"的形式出現(xiàn)
for (int i = 0; i < sLength; i++) { //循環(huán)每一個(gè)字節(jié)
if (i + 2 < sLength) {
/**
* aByte[i] & 0xF0 == 0xE0 ---> 判斷當(dāng)前字節(jié)是否以“1110”的形式開(kāi)始;
* aByte[i + 1] & 0xC0 == 0x80 ---> 判斷下一個(gè)字節(jié)是否以“10”的形式開(kāi)始;
* aByte[i + 2] & 0xC0 == 0x80 ---> 判斷下下一個(gè)字節(jié)是否以“10”的形式開(kāi)始。
* 假如條件都滿足,則表示此斷字節(jié)進(jìn)行了utf-8編碼,則將對(duì)其進(jìn)行解碼操作(即轉(zhuǎn)
* 化為unicode編碼)
*/
if ((aByte[i] & 0xF0) == 0xE0 && (aByte[i + 1] & 0xC0) == 0x80 &&
(aByte[i + 2] & 0xC0) == 0x80) {
/**
* 將當(dāng)前字節(jié) 1110 xxxx 轉(zhuǎn)化為 xxxx 000000 000000 的形式,具體步驟為:
* 1110 xxxx << 12 = xxxx 000000 000000
* 1110 0100 << 12 = 0100 000000 000000
*/
sChar = (char) (aByte[i] << 12);
/**
* 將 前兩個(gè)字節(jié) 轉(zhuǎn)化為 xxxx xxxxxx 000000 的形式,具體步驟為:
* 10 xxxxxx & 0x003F = 0000 000000 xxxxxx
* 10 111000 & 0x003F = 0000 000000 111000
*
* 0000 000000 xxxxxx << 6 = 0000 xxxxxx 000000
* 0000 000000 111000 << 6 = 0000 111000 000000
*
* xxxx 000000 000000 | 0000 xxxxxx 000000 = xxxx xxxxxx 000000
* 0100 000000 000000 | 0000 111000 000000 = 0100 111000 000000
*/
sChar = (char) ((((aByte[i + 1] & 0x003F) << 6) | sChar));
/**
* 將此三個(gè)字節(jié)轉(zhuǎn)化為 xxxx xxxxxx xxxxxx 的形式,具體步驟為:
* 10 xxxxxx & 0x003F = 0000 0000 00 xxxxxx
* 10 101101 & 0x003F = 0000 0000 00 101101
*
* xxxx xxxxxx 000000 | 0000 000000 xxxxxx = xxxx xxxxxx xxxxxx
* 0100 111000 000000 | 0000 000000 101101 = 0100 111000 101101
*/
sChar = (char) ((aByte[i + 2] & 0x003F) | sChar);
i = i + 2;
sUnicodeStringBuffer.append(sChar);
} else {
sUnicodeStringBuffer.append((char) aByte[i]);
}
}
}
return sUnicodeStringBuffer.toString();
}
(代碼4-1 將UTF-8的一種編碼方式轉(zhuǎn)換為UNICODE)
該代碼是僅轉(zhuǎn)換以“1110xxxx 10xxxxxx 10xxxxxx”形式進(jìn)行UTF-8編碼過(guò)的字節(jié)流,而要徹底的將UTF-8編碼轉(zhuǎn)換為UNICODE編碼,則需要對(duì)表3-2(Unicode和 UTF-8之間的轉(zhuǎn)換關(guān)系)中的六種編碼方式進(jìn)行處理。在java中,Unicode代碼點(diǎn)的作用范圍是U+0000至U+10FFFF之間的字符值,所以表3-2中的前四種編碼方式在java中有效,前三種編碼方式(BOM)可以直接將編碼前的兩個(gè)字節(jié)(1個(gè)單位的char)提取出來(lái),組成一個(gè) char,以達(dá)到解碼的目的,具體實(shí)現(xiàn)代碼為:
/**
* 將UTF-8編碼解碼
* @param aByte byte[]
* @return String
*/
public static String changeUtf8ToUnicode(byte[] aByte) {
StringBuffer sUnicodeStringBuffer = new StringBuffer();
int sLength = aByte.length;
int sInt_1, sInt_2, sInt_3, sInt_4, sInt_5, sInt_6;
for (int i = 0; i < sLength; i++) {
sInt_1 = (int) aByte[i] & 0xff;
switch (sInt_1 >> 4) {
case 0:
case 1:
case 2:
case 3:
case 4:
case 5:
case 6:
case 7:
/* 0xxxxxxx*/
sUnicodeStringBuffer.append((char) aByte[i]);
break;
case 12:
case 13:
/* 110x xxxx 10xx xxxx*/
if (i + 1 < sLength) {
sInt_2 = (char) aByte[i + 1];
if ((sInt_2 & 0xC0) == 0x80) {
sUnicodeStringBuffer.append((char)(((sInt_1 & 0x1F) << 6)
| (sInt_2 & 0x3F)));
i ++;
}
}
break;
case 14:
/* 1110 xxxx 10xx xxxx 10xx xxxx */
if (i + 2 < sLength) {
sInt_2 = (int) aByte[i + 1];
sInt_3 = (int) aByte[i + 2];
if (((sInt_2 & 0xC0) == 0x80) || ((sInt_3 & 0xC0) == 0x80)) {
sUnicodeStringBuffer.append((char)(((sInt_1 & 0x0F) << 12) | ((sInt_2 & 0x3F) << 6) | ((sInt_3 & 0x3F) << 0)));
i = i + 2;
}
}
break;
}
}
return sUnicodeStringBuffer.toString();
}
(代碼4-2 將UTF-8的前三種編碼方式[BOM]還原為UNICODE)
前三種編碼方式,即從U-00000000 - U- 0000FFFF區(qū)域,包含了99.9%的日常使用字符,一般進(jìn)行UTF-8與UNICODE的轉(zhuǎn)換時(shí),僅考慮前三中編碼方式便可。
第四個(gè)編碼方式(U-00010000 – U-001FFFFF)則需要使用增補(bǔ)字符進(jìn)行處理。下面將具體講解java平臺(tái)中的增補(bǔ)字符。
4.2.3 Java平臺(tái)中的增補(bǔ)字符
增補(bǔ)字符是Unicode標(biāo)準(zhǔn)中代碼點(diǎn)超出U+FFFF的字符,而 java中的增補(bǔ)字符則是指Unicode代碼點(diǎn)的作用范圍在U+FFFF至U+10FFFF之間的字符值。
4.2.3.1 Java平臺(tái)中增補(bǔ)字符出現(xiàn)背景
Unicode最初設(shè)計(jì)是作為一種固定寬度的16位字符編碼。在 Java編程語(yǔ)言中,基本數(shù)據(jù)類型char初衷是通過(guò)提供一種簡(jiǎn)單的、能夠包含任何字符的數(shù)據(jù)類型來(lái)充分利用這種設(shè)計(jì)的優(yōu)點(diǎn)。不過(guò),現(xiàn)在看來(lái),16位編碼的所有65,536 個(gè)字符并不能完全表示全世界所有正在使用或曾經(jīng)使用的字符。于是,Unicode 標(biāo)準(zhǔn)已擴(kuò)展到包含多達(dá) 1,112,064 個(gè)字符。那些超出原來(lái)的 16 位限制的字符被稱作增補(bǔ)字符。Unicode 標(biāo)準(zhǔn) 2.0 版是第一個(gè)包含啟用增補(bǔ)字符設(shè)計(jì)的版本,但是,直到 3.1 版才收入第一批增補(bǔ)字符集。由于 J2SE 的 5.0 版必須支持 Unicode 標(biāo)準(zhǔn) 4.0 版,因此它必須支持增補(bǔ)字符。
對(duì)增補(bǔ)字符的支持也可能會(huì)成為東亞市場(chǎng)的一個(gè)普遍商業(yè)要求。政府應(yīng)用程序會(huì)需要這些增補(bǔ)字符,以正確表示一些包含罕見(jiàn)中文字符的姓名。出版應(yīng)用程序可能會(huì)需要這些增補(bǔ)字符,以表示所有的古代字符和變體字符。中國(guó)政府要求支持GB18030(一種對(duì)整個(gè) Unicode字符集進(jìn)行編碼的字符編碼標(biāo)準(zhǔn)),因此,如果是Unicode 3.1版或更新版本,則將包括增補(bǔ)字符。臺(tái)灣標(biāo)準(zhǔn) CNS-11643包含的許多字符在 Unicode 3.1 中列為增補(bǔ)字符。香港政府定義了一種針對(duì)粵語(yǔ)的字符集,其中的一些字符是Unicode中的增補(bǔ)字符。最后,日本的一些供應(yīng)商正計(jì)劃利用增補(bǔ)字符空間中大量的專用空間收入50,000多個(gè)日文漢字字符變體,以便從其專有系統(tǒng)遷移至基于Java平臺(tái)的解決方案。
因此,Java平臺(tái)不僅需要支持增補(bǔ)字符,而且必須使應(yīng)用程序能夠方便地做到這一點(diǎn)。由于增補(bǔ)字符打破了Java編程語(yǔ)言的基礎(chǔ)設(shè)計(jì)構(gòu)想,而且可能要求對(duì)編程模型進(jìn)行根本性的修改,因此,Java Community Process召集了一個(gè)專家組,以期找到一個(gè)適當(dāng)?shù)慕鉀Q方案。該小組被稱為JSR-204專家組,使用 Unicode增補(bǔ)字符支持的Java 技術(shù)規(guī)范請(qǐng)求的編號(hào)。從技術(shù)上來(lái)說(shuō),該專家組的決定僅適用于J2SE平臺(tái),但是由于 Java 2平臺(tái)企業(yè)版 (J2EE)處于 J2SE 平臺(tái)的最上層,因此它可以直接受益,我們期望Java 2平臺(tái)袖珍版 (J2ME)的配置也采用相同的設(shè)計(jì)方法。
4.2.3.2 Java平臺(tái)中增補(bǔ)字符的設(shè)計(jì)方法
JSR-204 專家組必須作出的主要決定是如何在Java API中表示增補(bǔ)字符,包括單個(gè)字符和所有形式的字符序列。專家組考慮并排除了多種方法:
* 重新定義基本類型char,使其具有32位,這樣也會(huì)使所有形式的 char序列成為UTF-32序列;
* 在現(xiàn)有的16位類型char的基礎(chǔ)上,為字符引入一種新的32位基本類型(例如,char32)。所有形式的Char序列均基于 UTF-16;
* 在現(xiàn)有的16位類型char的基礎(chǔ)上,為字符引入一種新的32位基本類型(例如,char32)。String和StringBuffer 接受并行API,并將它們解釋為UTF-16序列或UTF-32序列;其他char序列繼續(xù)基于UTF-16;
* 使用int表示增補(bǔ)的代碼點(diǎn)。String和StringBuffer接受并行API,并將它們解釋為UTF-16序列或UTF-32序列;其他char 序列繼續(xù)基于UTF-16;
* 使用代理char對(duì),表示增補(bǔ)代碼點(diǎn)。所有形式的char序列基于UTF-16;
* 引入一種封裝字符的類。String和StringBuffer接受新的API,并將它們解釋為此類字符的序列;
* 使用一個(gè)CharSequence實(shí)例和一個(gè)索引的組合表示代碼點(diǎn)。
(表4-1 專家組考慮的增補(bǔ)字符設(shè)計(jì)方法)
在這些方法中,一些在早期就被排除了。例如,重新定義基本類型 char,使其具有32位,這對(duì)于全新的平臺(tái)可能會(huì)非常有吸引力,但是,對(duì)于J2SE來(lái)說(shuō),它會(huì)與現(xiàn)有的Java虛擬機(jī)、序列化和其他接口不兼容,更不用說(shuō)基于 UTF-32的字符串要使用兩倍于基于UTF-16的字符串的內(nèi)存了。添加一種新類型的 char32可能會(huì)簡(jiǎn)單一些,但是仍然會(huì)出現(xiàn)虛擬機(jī)和序列化方面的問(wèn)題。而且,語(yǔ)言更改通常需要比API更改有更長(zhǎng)的提前期,因此,前面兩種方法會(huì)對(duì)增補(bǔ)字符支持帶來(lái)無(wú)法接受的延遲。為了在余下的方法中篩選出最優(yōu)方案,實(shí)現(xiàn)小組使用四種不同的方法,在大量進(jìn)行低層字符處理的代碼(java.util.regex包)中實(shí)現(xiàn)了對(duì)增補(bǔ)字符支持,并對(duì)這四種方法的難易程度和運(yùn)行表現(xiàn)進(jìn)行了比較。最終,專家組確定了一種分層的方法:
* 使用基本類型int在低層API中表示代碼點(diǎn),例如Character類的靜態(tài)方法。
* 將所有形式的char序列均解釋為UTF-16序列,并促進(jìn)其在更高層級(jí)API中的使用。
* 提供API,以方便在各種char和基于代碼點(diǎn)的表示法之間的轉(zhuǎn)換。
(表4-2 專家組最終確定的增補(bǔ)字符設(shè)計(jì)方法)
在需要時(shí),此方法既能夠提供一種概念簡(jiǎn)明且高效的單個(gè)字符表示法,又能夠充分利用通過(guò)改進(jìn)可支持增補(bǔ)字符的現(xiàn)有API。同時(shí),還能夠促進(jìn)字符序列在單個(gè)字符上的應(yīng)用,這一點(diǎn)一般對(duì)于國(guó)際化的軟件很有好處。
4.2.3.2 開(kāi)放的增補(bǔ)字符——基于代碼點(diǎn)的API
新增的低層API分為兩大類:用于各種char和基于代碼點(diǎn)的表示法之間轉(zhuǎn)換的方法和用于分析和映射代碼點(diǎn)的方法。
最基本的轉(zhuǎn)換方法是Character.toCodePoint(char high, char low)(用于將兩個(gè)UTF-16代碼單元轉(zhuǎn)換為一個(gè)代碼點(diǎn))和 Character.toChars(int codePoint)(用于將指定的代碼點(diǎn)轉(zhuǎn)換為一個(gè)或兩個(gè) UTF-16 代碼單元,然后封裝到一個(gè)char[]內(nèi)。不過(guò),由于大多數(shù)情況下文本以字符序列的形式出現(xiàn),因此,另外提供codePointAt和 codePointBefore方法,用于將代碼點(diǎn)從各種字符序列表示法中提取出來(lái):Character.codePointAt(char[] a, int index)和String.codePointBefore(int index)是兩種典型的例子。在將代碼點(diǎn)插入字符序列時(shí),大多數(shù)情況下均有一些針對(duì)StringBuffer和StringBuilder類的 appendCodePoint(int codePoint)方法,以及一個(gè)用于提取表示代碼點(diǎn)的int[]的String構(gòu)建器。
幾種用于分析代碼單元和代碼點(diǎn)的方法有助于轉(zhuǎn)換過(guò)程:Character類中的 isHighSurrogate和isLowSurrogate方法可以識(shí)別用于表示增補(bǔ)字符的char 值;charCount(int codePoint)方法可以確定是否需要將某個(gè)代碼點(diǎn)轉(zhuǎn)換為一個(gè)或兩個(gè)char。
但是,大多數(shù)基于代碼點(diǎn)的方法均能夠?qū)λ蠻nicode字符實(shí)現(xiàn)基于char的舊方法對(duì)BMP字符所實(shí)現(xiàn)的功能。以下是一些典型例子:
* Character.isLetter(int codePoint)可根據(jù)Unicode標(biāo)準(zhǔn)識(shí)別字母;
* Character.isJavaIdentifierStart(int codePoint)可根據(jù)Java語(yǔ)言規(guī)范確定代碼點(diǎn)是否可以啟動(dòng)標(biāo)識(shí)符;
* Character.UnicodeBlock.of(int codePoint)可搜索代碼點(diǎn)所屬的Unicode字符子集;
* Character.toUpperCase(int codePoint)可將給定的代碼點(diǎn)轉(zhuǎn)換為其大寫(xiě)等值字符。盡管此方法能夠支持增補(bǔ)字符,但是它仍然不能解決根本的問(wèn)題,即在某些情況下,逐個(gè)字符的轉(zhuǎn)換無(wú)法正確完成。例如,德文字符“??”應(yīng)該轉(zhuǎn)換為“SS”,這需要使用String.toUpperCase方法;
(表4-3 代碼點(diǎn)方法對(duì)BOM字符的處理實(shí)例)
注意:大多數(shù)接受代碼點(diǎn)的方法并不檢查給定的int值是否處于有效的 Unicode代碼點(diǎn)范圍之內(nèi)(如上所述,只有0x0至0x10FFFF之間的范圍是有效的)。在大多數(shù)情況下,該值是以確保其有效的方法產(chǎn)生的,在這些低層API中反復(fù)檢查其有效性可能會(huì)對(duì)系統(tǒng)性能造成負(fù)面的影響。在無(wú)法確保有效性的情況下,應(yīng)用程序必須使用 Character.isValidCodePoint方法確保代碼點(diǎn)有效。大多數(shù)方法對(duì)于無(wú)效的代碼點(diǎn)采取的行為沒(méi)有特別加以指定,不同的實(shí)現(xiàn)可能會(huì)有所不同。
API包含許多簡(jiǎn)便的方法,這些方法可使用其他低層的API實(shí)現(xiàn),但是專家組覺(jué)得,這些方法很常用,將它們添加到J2SE平臺(tái)上很有意義。不過(guò),專家組也排除了一些建議的簡(jiǎn)便方法,這給我們提供了一次展示自己實(shí)現(xiàn)此類方法能力的機(jī)會(huì)。例如,專家組經(jīng)過(guò)討論,排除了一種針對(duì)String類的新構(gòu)建器(該構(gòu)建器可以創(chuàng)建一個(gè)保持單個(gè)代碼點(diǎn)的String)。以下是使應(yīng)用程序使用現(xiàn)有的API提供功能的一種簡(jiǎn)便方法:
/**
* 創(chuàng)建僅含有指定代碼點(diǎn)的新 String。
*/
String newString(int codePoint) {
return new String(Character.toChars(codePoint));
}
(代碼4-3 使應(yīng)用程序使用現(xiàn)有的API提供功能的一種簡(jiǎn)便方法)
您會(huì)注意到,在這個(gè)簡(jiǎn)單的實(shí)現(xiàn)中,toChars方法始終創(chuàng)建一個(gè)中間數(shù)列,該數(shù)列僅使用一次即立即丟棄。如果該方法在您的性能評(píng)估中出現(xiàn),您可能會(huì)希望將其優(yōu)化為針對(duì)最為普通的情況,即該代碼點(diǎn)為BMP字符:
/**
* 創(chuàng)建僅含有指定代碼點(diǎn)的新String。
* 針對(duì)BMP字符優(yōu)化的版本。
*/
String newString(int codePoint) {
if (Character.charCount(codePoint) == 1) {
return String.valueOf((char) codePoint);
} else {
return new String(Character.toChars(codePoint));
}
}
(代碼4-4 使應(yīng)用程序使用現(xiàn)有的API提供功能的最為普通的情況)
或者,如果您需要?jiǎng)?chuàng)建許多個(gè)這樣的string,則可能希望編寫(xiě)一個(gè)重復(fù)使用toChars方法所使用的數(shù)列的通用版本:
/**
* 創(chuàng)建每一個(gè)均含有一個(gè)指定
* 代碼點(diǎn)的新 String。
* 針對(duì) BMP 字符優(yōu)化的版本。
*/
String[] newStrings(int[] codePoints) {
String[] result = new String[codePoints.length];
char[] codeUnits = new char[2];
for (int i = 0; i < codePoints.length; i++) {
int count = Character.toChars(codePoints[i], codeUnits, 0);
result[i] = new String(codeUnits, 0, count);
}
return result;
}
(代碼4-5 toChars方法所使用的數(shù)列的通用版本)
不過(guò),最終您可能會(huì)發(fā)現(xiàn),您需要的是一個(gè)完全不同的解決方案。新的構(gòu)建器String(intcodePoint)實(shí)際上建議作為String.valueOf(char)的一個(gè)基于代碼點(diǎn)的備選方案。在很多情況下,此方法用于消息生成的環(huán)境,例如:
System.out.println("Character " + String.valueOf(char) + " is invalid.");
(代碼4-6 消息生成的環(huán)境)
新的格式化API支持增補(bǔ)文字,提供一種更加簡(jiǎn)單的備選方案:
System.out.printf("Character %c is invalid.%n", codePoint);
(代碼4-7 增補(bǔ)文字的備選方案)
使用此高層API不僅簡(jiǎn)捷,而它有很多特殊的優(yōu)點(diǎn):它可以避免串聯(lián)(串聯(lián)會(huì)使消息很難本地化),并將需要移進(jìn)資源包(resourcebundle)的字符串?dāng)?shù)量從兩個(gè)減少到一個(gè)。
在Java編程語(yǔ)言源文件中,如果使用可以直接表示增補(bǔ)字符的字符編碼,則使用增補(bǔ)字符最為方便。UTF-8是最佳的選擇。在所使用的字符編碼無(wú)法直接表示字符的情況下,Java編程語(yǔ)言提供一種Unicode轉(zhuǎn)義符語(yǔ)法。此語(yǔ)法沒(méi)有經(jīng)過(guò)增強(qiáng),無(wú)法直接表示增補(bǔ)字符。而是使用兩個(gè)連續(xù)的Unicode轉(zhuǎn)義符將其表示為UTF-16字符表示法中的兩個(gè)編碼單元。例如,字符U+20000寫(xiě)作“\uD840\uDC00”。最好是寫(xiě)入支持所需增補(bǔ)字符的編碼,然后使用一種工具(如native2ascii)將其轉(zhuǎn)換為轉(zhuǎn)義序列。
遺憾的是,由于其編碼問(wèn)題,屬性文件仍局限于ISO8859-1(除非您的應(yīng)用程序使用新的XML格式)。這意味著您始終必須對(duì)增補(bǔ)字符使用轉(zhuǎn)義序列,而且可能要使用不同的編碼進(jìn)行編寫(xiě),然后使用諸如native2ascii的工具進(jìn)行轉(zhuǎn)換。
4.2.3.3 經(jīng)修訂的UTF-8
Java平臺(tái)對(duì)經(jīng)修訂的UTF-8已經(jīng)很熟悉,但是,問(wèn)題是應(yīng)用程序開(kāi)發(fā)人員在可能包含增補(bǔ)字符的文本和UTF-8之間進(jìn)行轉(zhuǎn)換時(shí)需要更加留神。需要特別注意的是,某些J2SE接口使用的編碼與UTF-8相似但與其并不兼容。以前,此編碼有時(shí)被稱為“JavamodifiedUTF-8”(經(jīng)Java修訂的UTF-8)或(錯(cuò)誤地)直接稱為“UTF-8”。對(duì)于J2SE5.0,其說(shuō)明文檔正在更新,此編碼將統(tǒng)稱為“modifiedUTF-8”(經(jīng)修訂的 UTF-8)。
經(jīng)修訂的UTF-8和標(biāo)準(zhǔn)UTF-8之間之所以不兼容,其原因有兩點(diǎn)。其一,經(jīng)修訂的UTF-8將字符U+0000表示為雙字節(jié)序列0xC00x80,而標(biāo)準(zhǔn)UTF-8使用單字節(jié)值0x0。其二,經(jīng)修訂的UTF-8通過(guò)對(duì)其UTF-16表示法的兩個(gè)代理代碼單元單獨(dú)進(jìn)行編碼表示增補(bǔ)字符。每個(gè)代理代碼單元由三個(gè)字節(jié)來(lái)表示,共有六個(gè)字節(jié)。而標(biāo)準(zhǔn)UTF-8使用單個(gè)四字節(jié)序列表示整個(gè)字符。
Java虛擬機(jī)及其附帶的接口(如Java本機(jī)接口、多種工具接口或Java類文件)在java.io.DataInput和DataOutput接口和類中使用經(jīng)修訂的UTF-8實(shí)現(xiàn)或使用這些接口和類,并進(jìn)行序列化。Java本機(jī)接口提供與經(jīng)修訂的UTF-8之間進(jìn)行轉(zhuǎn)換的例程。而標(biāo)準(zhǔn)UTF-8由 String類、java.io.InputStreamReader和OutputStreamWriter類、java.nio.charset設(shè)施 (facility)以及許多其上層的API提供支持。
由于經(jīng)修訂的UTF-8與標(biāo)準(zhǔn)的UTF-8不兼容,因此切勿同時(shí)使用這兩種版本的編碼。經(jīng)修訂的UTF-8只能與上述的Java接口配合使用。在任何其他情況下,尤其對(duì)于可能來(lái)自非基于Java平臺(tái)的軟件的或可能通過(guò)其編譯的數(shù)據(jù)流,必須使用標(biāo)準(zhǔn)的UTF-8。需要使用標(biāo)準(zhǔn)的UTF-8時(shí),則不能使用 Java本機(jī)接口例程與經(jīng)修訂的UTF-8進(jìn)行轉(zhuǎn)換。
4.2.3.4 在應(yīng)用程序內(nèi)支持增補(bǔ)字符
對(duì)于僅以各種形式char序列([char[]、java.lang.CharSequence實(shí)現(xiàn)、 java.text.CharacterIterator實(shí)現(xiàn))處理文本和僅使用接受和退回序列(如char序列)的JavaAPI的應(yīng)用程序,可能根本不需要進(jìn)行任何更改。Java平臺(tái)API的實(shí)現(xiàn)應(yīng)該能夠處理增補(bǔ)字符。
對(duì)于本身解釋單個(gè)字符、將單個(gè)字符傳送給Java平臺(tái)API或調(diào)用能夠返回單個(gè)字符的方法的應(yīng)用程序,則需要考慮這些字符的有效值。在很多情況下,往往不要求支持增補(bǔ)字符。例如,如果某應(yīng)用程序搜索char序列中的HTML標(biāo)記,并逐一檢查每個(gè)char,它會(huì)知道這些標(biāo)記僅使用BasicLatin字符子集中的字符。如果所搜索的文本含有增補(bǔ)字符,則這些字符不會(huì)與標(biāo)記字符混淆,因?yàn)閁TF-16使用代碼單元表示增補(bǔ)字符,而代碼單元的值不會(huì)用于BMP字符。
只有在某應(yīng)用程序本身解釋單個(gè)字符、將單個(gè)字符傳送給Java平臺(tái)API或調(diào)用能夠返回單個(gè)字符的方法且這些字符可能為增補(bǔ)字符時(shí),才必須更改該應(yīng)用程序。在提供使用char序列的并行API時(shí),最好轉(zhuǎn)而使用此類API。在其他情況下,有必要使用新的API在char和基于代碼點(diǎn)的表示法之間進(jìn)行轉(zhuǎn)換,并調(diào)用基于代碼點(diǎn)的API。當(dāng)然,如果您發(fā)現(xiàn)在J2SE5.0中有更新、更方便的API,使您能夠支持增補(bǔ)字符并同時(shí)簡(jiǎn)化代碼(如上格式化范例中所述),則沒(méi)有必要這樣做。
您可能會(huì)猶豫,是將所有文本轉(zhuǎn)換為代碼點(diǎn)表示法(即int[])然后在該表示法中處理,還是在大多數(shù)情況下仍采用char序列,僅在需要時(shí)轉(zhuǎn)換為代碼點(diǎn),兩者之間孰優(yōu)孰劣很難確定。當(dāng)然,總體來(lái)說(shuō),Java平臺(tái)API相對(duì)于char序列肯定具有一定的優(yōu)勢(shì),而且采用Java平臺(tái)API可以節(jié)省內(nèi)存空間。
對(duì)于需要與UTF-8之間進(jìn)行轉(zhuǎn)換的應(yīng)用程序,還需要認(rèn)真考慮是需要標(biāo)準(zhǔn)的UTF-8還是經(jīng)修訂的UTF-8,并針對(duì)每種UTF-8采用適當(dāng)?shù)腏ava平臺(tái)。“經(jīng)修訂的UTF-8”部分介紹進(jìn)行正確選擇所需的信息。