(十二)UTF-16編碼方式
UTF-16編碼方式源于UCS-2(Universal Character Set coded in 2 octets、2-byte Universal Character Set)。而UCS-2,是早期遺留下來的歷史產(chǎn)物。
UCS-2將字符編號(hào)直接映射為字符編碼(CEF,而非CES,詳見前文中對(duì)現(xiàn)代字符編碼模型的解釋),亦即字符編號(hào)就是字符編碼,中間沒有經(jīng)過特別的編碼算法轉(zhuǎn)換。因此,從現(xiàn)代字符編碼模型的角度來看的話,此時(shí)并沒有將編號(hào)字符集CCS與字符編碼方式CEF作嚴(yán)格區(qū)分,既可以將UCS-2看作是編號(hào)字符集CCS中的字符編號(hào),也可以看作是字符編碼方式CEF中的字符編碼。
后來,隨著Unicode聯(lián)盟與ISO/IEC就創(chuàng)建全球統(tǒng)一的單一通用字符集進(jìn)行合作,Unicode字符集與UCS字符集逐漸相互融合,兩者最終基本保持了一致。
這之后,Unicode逐漸占據(jù)了主導(dǎo)地位,并引入了UTF-16編碼方式。為什么要引入U(xiǎn)TF-16編碼方式呢?
前文已經(jīng)介紹過了,Unicode字符集(CCS)到目前為止定義了包括1個(gè)基本平面BMP和16個(gè)增補(bǔ)平面SP在內(nèi)的共17個(gè)平面。
每個(gè)平面的碼點(diǎn)數(shù)量為2^16=65536個(gè),因此17個(gè)平面的碼點(diǎn)總數(shù)為共6553617=1114112個(gè)。其中,基本平面碼點(diǎn)為65536個(gè)(碼點(diǎn)編號(hào)范圍為0x0000~0xFFFF),增補(bǔ)平面碼點(diǎn)為1114112-65536=6553616=1048576個(gè)(碼點(diǎn)編號(hào)范圍為0x10000~0x10FFFF)。
很明顯,簡(jiǎn)單地用一個(gè)16位碼元肯定無法表示所有17個(gè)平面的這么多碼點(diǎn)(因?yàn)?^16=65536,而碼點(diǎn)總數(shù)為65536*17=1114112)。而UCS-2,正是用兩個(gè)字節(jié)共16位來表示一個(gè)字符的。為支持字符編號(hào)超過U+FFFF的增補(bǔ)字符,擴(kuò)展勢(shì)在必行。
UCS因而又提出了UCS-4,即用四個(gè)字節(jié)共32位來表示一個(gè)字符(此時(shí)UCS-4同樣既可認(rèn)為是編號(hào)字符集CCS中的字符編號(hào),也可認(rèn)為是字符編碼方式CEF中的字符編碼)。但碼元也因此從16位擴(kuò)展到了32位。
而Unicode卻提出了不同的擴(kuò)展方式——代理機(jī)制。具體而言,就是為了能以一個(gè)統(tǒng)一的16位碼元同時(shí)編碼基本平面以及增補(bǔ)平面中的字符碼點(diǎn)編號(hào),Unicode設(shè)計(jì)引入了UTF-16編碼方式,并且通過代理機(jī)制實(shí)現(xiàn)了擴(kuò)展。
UTF-16編碼方式的引入,從現(xiàn)代字符編碼模型的角度來看的話,徹底將編號(hào)字符集CCS與字符編碼方式CEF作了嚴(yán)格區(qū)分。也就是說,在UTF-16編碼方式中,編號(hào)字符集CCS中的字符編號(hào)與字符編碼方式CEF中的字符編碼不再僅僅是簡(jiǎn)單的直接映射關(guān)系。
具體來說,就是Unicode字符集基本平面BMP中的字符(大致相當(dāng)于UCS字符集中的UCS-2字符,但必須除開U+D800~U+DFFF這一在Unicode字符集BMP中稱之為代理碼點(diǎn)的部分),仍然是直接映射關(guān)系,亦即這部分字符的字符編號(hào)與字符編碼是等同的。
但Unicode字符集增補(bǔ)平面中的字符(大致相當(dāng)于UCS字符集UCS-4字符中除開UCS-2字符的部分,因?yàn)閺V義上的UCS-4字符實(shí)際上包含了UCS-2字符,當(dāng)然狹義上的UCS-4字符不包括UCS-2字符),卻不是直接映射關(guān)系,而是必須通過代理機(jī)制這一編碼算法的轉(zhuǎn)換,亦即這部分字符的字符編號(hào)與字符編碼不是等同的。
因此,在Unicode引入了UTF-16編碼方式之后,站在現(xiàn)代字符編碼模型的角度上來看的話,再將UCS-2和UCS-4直接稱之為字符編碼方式CEF已不是很合適,更多的應(yīng)該是編號(hào)字符集CCS中的概念(當(dāng)然,在了解其歷史原因之后,將UCS-2和UCS-4同時(shí)理解為編號(hào)字符集CCS和字符編碼方式CEF也未嘗不可);而若將UCS-2等同于UTF-16,將UCS-4等同于UTF-32(后文會(huì)有介紹),顯然也是不合適的。
UTF-16中的所謂代理機(jī)制,實(shí)際上就是用兩個(gè)對(duì)應(yīng)于基本平面BMP代理區(qū)(Surrogate Zone)中的碼點(diǎn)編號(hào)的16位碼元來表示一個(gè)增補(bǔ)平面碼點(diǎn),這兩個(gè)用來表示一個(gè)增補(bǔ)平面碼點(diǎn)的特殊16位碼元被稱之為代理對(duì)(Surrogate Pair)(解釋詳見后文《UTF-16究竟是如何編碼的——UTF-16的編碼算法詳解》)
UTF-16編碼方式及其代理機(jī)制是在Unicode 2.0中為支持字符編號(hào)超過U+FFFF的增補(bǔ)字符而引入的,于是從此就由UCS-2的等寬(16位)碼元序列編碼方式(如前文所述,從現(xiàn)代字符編碼模型的角度來看的話,UCS-2更多是的編號(hào)字符集CCS中的概念,但考慮到其歷史原因,稱之為字符編碼方式CEF亦未嘗不可,下同,不再贅述),變成了UTF-16的變寬(16位或32位)碼元序列編碼方式。不過,碼元依然保持了16位不變。
UCS-2所編碼的字符集中的U+D800~U+DFFF這部分代理碼點(diǎn)除外的話,UTF-16所編碼的字符集可看成是UCS-2所編碼的字符集的父集。
在沒有引入增補(bǔ)平面字符之前,UTF-16與UCS-2(U+D800~U+DFFF這部分代理碼點(diǎn)除外)的編碼完全相同。但當(dāng)引入增補(bǔ)平面字符后,UTF-16與UCS-2的編碼就不完全相同了(事實(shí)上,由于UCS-2只有兩個(gè)字節(jié),根本無法編碼增補(bǔ)平面字符)。
現(xiàn)在若有軟件聲稱自己支持UCS-2編碼,那相當(dāng)于是在暗示其僅支持UCS字符集或Unicode字符集中的基本平面字符,而不能支持增補(bǔ)平面字符。
所以說,UTF-16是變長(zhǎng)編碼方式,每個(gè)字符編碼為16位或32位;而UCS-2是定長(zhǎng)編碼方式,每個(gè)字符編碼固定為16位。但兩者的碼元卻都是16位的(而UTF-32和狹義的UCS-4的碼元都是32位的)。
另外,UTF-16中,大部分漢字采用兩個(gè)字節(jié)編碼,少量不常用漢字采用四個(gè)字節(jié)編碼。
Windows 2000及之后的版本是支持UTF-16的,之前的Windows NT/95/98/ME是只支持UCS-2的。
作為邏輯意義上的UTF-16編碼(碼元序列),由于歷史的原因,在映射為物理意義上的字節(jié)序列時(shí),分為UTF-16BE(Big Endian)、UTF-16LE(Little Endian)兩種情況。比如,“ABC”這三個(gè)字符的UTF-16編碼(碼元序列)為:00 41 00 42 00 43;其對(duì)應(yīng)的各種字節(jié)序列如下:

Windows從NT時(shí)代開始就采用了UTF-16編碼方式,很多流行的編程平臺(tái),例如.Net、Java、Qt還有Mac下的Cocoa等都是使用UTF-16作為基礎(chǔ)的字符編碼。例如代碼中的字符串,在內(nèi)存中相應(yīng)的字節(jié)流就是UTF-16字節(jié)序列的。(注意,UTF-16編碼在Windows環(huán)境中被誤用為“widechar”和“Unicode”的同義詞)
UTF-16一方面使用變長(zhǎng)碼元序列的編碼方式,相較于定長(zhǎng)碼元序列的UTF-32算法更復(fù)雜(甚至比同樣是變長(zhǎng)碼元序列的UTF-8也更為復(fù)雜,因?yàn)橐肓霜?dú)特的代理對(duì)這樣的代理機(jī)制);另一方面仍然占用過多字節(jié),比如ASCII字符也同樣需要占用兩個(gè)字節(jié),相較于UTF-8更浪費(fèi)空間和帶寬。
因此,UTF-16在Unicode字符集的三大編碼方式(UTF-8、UTF-16、UTF-32)中表現(xiàn)較為糟糕。它的存在是歷史原因造成的,引起了很多混亂。不過由于其推出時(shí)間最早,已被應(yīng)用于大量環(huán)境中,目前雖然不被推薦使用,但長(zhǎng)期來看,作為程序人員都不得不與之打交道。因而,對(duì)于其具體的編碼算法的了解是十分必要的,本系列文章的下一篇將詳細(xì)介紹其復(fù)雜的編碼算法(主要是代理編碼算法)。
(十三)UTF-16究竟是怎么編碼的
首先要注意的是,代理Surrogate是專屬于UTF-16編碼方式的一種機(jī)制,UTF-8和UTF-32是不用代理的。
如前文所述,為了讓UTF-16能繼續(xù)編碼基本平面后面的增補(bǔ)平面中的碼點(diǎn)值,于是擴(kuò)展了UTF-16編碼方式。
具體的擴(kuò)展方法就是為其增加了代理機(jī)制,用兩個(gè)對(duì)應(yīng)于基本平面碼點(diǎn)(即BMP代理區(qū)中的碼點(diǎn))的16位碼元來表示一個(gè)增補(bǔ)平面碼點(diǎn),這兩個(gè)用來表示一個(gè)增補(bǔ)平面碼點(diǎn)的特殊16位碼元就被稱為“代理對(duì)”。
如果要用簡(jiǎn)單的一句話來概括,就是——所有大于0xFFFF的碼點(diǎn)值(即增補(bǔ)平面碼點(diǎn)編號(hào),范圍為0x10000~0x10FFFF,十進(jìn)制為65536~1114111;注意,0xFFFF是十六位二進(jìn)制數(shù)的最大值的十六進(jìn)制表示)要編碼成UTF-16編碼方式的話,就必須使用代理機(jī)制(也就是用代理對(duì)來表示)。
在UTF-16編碼方式中,被合起來稱為“代理對(duì)”的這兩個(gè)16位碼元就其中的任一單個(gè)碼元而言,其實(shí)就直接對(duì)應(yīng)于基本平面BMP中的某一個(gè)碼點(diǎn)(即BMP中每一個(gè)碼點(diǎn)的值必然對(duì)應(yīng)于一個(gè)16位碼元的值,因?yàn)榛酒矫嬷械拇a點(diǎn)總數(shù)為2^16=65536個(gè),而16位碼元能表示的值也等于2^16=65536個(gè))。
這樣一來,就產(chǎn)生了沖突:某個(gè)UTF-16碼元到底是用于表示基本平面字符的碼元,還是用于表示增補(bǔ)平面字符的代理對(duì)中的代理碼元?
因此,為避免沖突,這些被用作“代理”的任一碼元所對(duì)應(yīng)的碼點(diǎn)在基本平面中均未定義字符,即均沒有指定字符。且形成“代理對(duì)”的兩個(gè)碼元所對(duì)應(yīng)的碼點(diǎn)其編號(hào)必定是連續(xù)的。
“代理”的真實(shí)含義或許就在于此:用兩個(gè)基本平面中未定義字符的連續(xù)碼點(diǎn)合起來“代為署理”增補(bǔ)平面中的碼點(diǎn)。
因此,基本平面中這些用作“代理”的碼點(diǎn)區(qū)域就被稱之為“代理區(qū)(Surrogate Zone)”,其碼點(diǎn)編號(hào)范圍為0xD800~0xDFFF(十進(jìn)制55296~57343),共2048個(gè)碼點(diǎn)。
增補(bǔ)平面一共有16個(gè)平面(即第2平面~第17平面),碼點(diǎn)編號(hào)范圍為0x10000~0x10FFFF(十進(jìn)制為65536~1114111,碼點(diǎn)總數(shù)為1048576個(gè))。用兩個(gè)代理碼元表示,第一個(gè)碼元的取值范圍為0xD800~0xDBFF(二進(jìn)制為1101 1000 0000 0000 ~ 1101 1011 1111 1111,十進(jìn)制為55296 ~ 56319),第二個(gè)碼元的取值范圍為0xDC00~0xDFFF(二進(jìn)制為1101 1100 0000 0000 ~ 1101 1111 1111 1111,十進(jìn)制為56320 ~ 57343)。
因此,增補(bǔ)平面的第一個(gè)碼點(diǎn)的編號(hào)0x10000其UTF-16編碼就是0xD800 0xDC00(即0x10000經(jīng)UTF-16編碼后的碼元序列為0xD800 0xDC00),其余類推。展現(xiàn)為二進(jìn)制形式后如下:
====代理碼元1==== ====代理碼元2====
1101 10pp ppxx xxxx 1101 11xx xxxx xxxx
其中代理碼元1中的110110、代理碼元2中的110111是定數(shù),p、x是變數(shù)。去掉定數(shù)后組合起來就是pppp xxxx xxxx xxxx xxxx,共20位(2^20=1048576),剛好能夠表示增補(bǔ)平面中的全部碼點(diǎn)(0x10000~0x10FFFF,共1048576個(gè))。其中pppp共4位,表示16個(gè)增補(bǔ)平面之一的編號(hào)(2^4=16);緊接著的16位x表示某個(gè)增補(bǔ)平面內(nèi)的某個(gè)碼點(diǎn)(2^16=65536,而65536*16=1048576)。
按照上面的編碼方式,代理對(duì)里面的兩個(gè)代理碼元分別稱之為高16位代理碼元(或稱為lead surrogates引導(dǎo)代理、前導(dǎo)代理),和低16位代理碼元(或稱為trail surrogates尾隨代理、后尾代理)。
由于引導(dǎo)代理和尾隨代理的值分別在0xD800~0xDBFF(十進(jìn)制為55296 ~ 56319)之間和0xDC00~0xDFFF(十進(jìn)制為56320 ~ 57343)之間,所以首尾兩個(gè)代理總共可以組合出(56319-55296+1)*(57343-56320+1)=1048576個(gè)代理對(duì),也就是總共可以表示1048576個(gè)增補(bǔ)碼點(diǎn),而目前Unicode標(biāo)準(zhǔn)所確定的16個(gè)增補(bǔ)平面的碼點(diǎn)總和也就是65536*16=1048576個(gè)。
從增補(bǔ)平面的碼點(diǎn)值通過基本平面中的代理對(duì)編碼為增補(bǔ)平面字符的碼元序列的具體算法如下:
1) 增補(bǔ)平面中的碼點(diǎn)值(0x10000~0x10FFFF,二進(jìn)制為0001 0000 0000 0000 0000~1 0000 1111 1111 1111 1111,對(duì)應(yīng)的碼點(diǎn)名稱為U+10000~U+10FFFF)減去0x10000(二進(jìn)制為0001 0000 0000 0000 0000),可得到20位長(zhǎng)的比特組(值的范圍為0x00000~0xFFFFF,二進(jìn)制為0000 0000 0000 0000 0000 ~ 1111 1111 1111 1111 1111);
2)將得到的20位長(zhǎng)的比特組分拆為兩部分:高位10比特和低位10比特;
3)20位長(zhǎng)的比特組中的高位10比特(值的范圍為0x000~0x3FF,二進(jìn)制為00 0000 0000~11 1111 1111)加上0xD800(二進(jìn)制為1101 1000 0000 0000),得到第一個(gè)代理碼元即引導(dǎo)代理(值的范圍是0xD800~0xDBFF,二進(jìn)制為1101 1000 0000 0000 ~ 1101 1011 1111 1111);
4)20位長(zhǎng)的比特組中的低位10比特(值范圍也是0x000~0x3FF,二進(jìn)制為00 0000 0000~11 1111 1111)加上0xDC00(二進(jìn)制為1101 1100 0000 0000),得到第二個(gè)代理碼元即尾隨代理(值的范圍是0xDC00~0xDFFF,二進(jìn)制為1101 1100 0000 0000 ~ 1101 1111 1111 1111);
5)將引導(dǎo)代理與尾隨代理按前后順序組合在一起成為“代理對(duì)”,就得到了增補(bǔ)平面字符的碼元序列。
例如,增補(bǔ)平面中碼點(diǎn)值為10437(碼點(diǎn)名稱為U+10437)的字符(?):
1)0x10437減去0x10000,結(jié)果為0x00437,二進(jìn)制為0000 0000 0100 0011 0111。
2)分拆成高10位值和低10位值兩部分:0000000001(即0x0001)及0000110111(即0x0037)。
3)添加0xD800到高位值,以形成高位的引導(dǎo)代理:0xD800 + 0x0001 = 0xD801(二進(jìn)制為1101 1000 0000 0001)。
4)添加0xDC00到低位值,以形成低位的尾隨代理:0xDC00 + 0x0037 = 0xDC37(二進(jìn)制為1101 1100 0011 0111)。
5)將高位的引導(dǎo)代理與低位的尾隨代理按前后順序組合在一起成為“代理對(duì)”,就得到了增補(bǔ)平面字符?(碼點(diǎn)名稱為U+10437)的碼元序列:1101 1000 0000 0001 1101 1100 0011 0111。
下表總結(jié)了該轉(zhuǎn)換。不同的顏色表示碼點(diǎn)值是如何被分布到UTF-16碼元序列中的,而由UTF-16編碼過程中加入的代理附加位則以不同的紅色(亮紅色與暗紅色)顯示:

顯然,增補(bǔ)平面中的碼點(diǎn)值從0x10000到0x10FFFF,共計(jì)0xFFFFF + 0x1個(gè),即1,048,576個(gè),剛好也就是需要20位來表示(2^20=1,048,576)。如果用兩個(gè)16位長(zhǎng)的碼元組成的序列來表示,意味著引導(dǎo)代理要容納上述20位中的前10位,尾隨代理要容納上述20位中的后10位。
另外,還要能夠根據(jù)每個(gè)16位碼元來直接判斷該碼元到底是屬于引導(dǎo)代理(標(biāo)志位為前6位11 0110,還剩下10位,因此總個(gè)數(shù)為2^10=1024個(gè)),還是屬于尾隨代理(標(biāo)志位為前6位11 0111,也剩下10位,因此總個(gè)數(shù)也是2^10=1024個(gè))。
為避免沖突,因此需要在基本多語言平面BMP中保留未定義Unicode字符的1024+1024=2048個(gè)碼點(diǎn),就可以容納引導(dǎo)代理與尾隨代理所需要的編號(hào)空間(碼點(diǎn)空間、代碼空間),也就是16個(gè)增補(bǔ)平面所需要的編號(hào)空間,共計(jì)1024*1024=2^20=1048576個(gè)碼點(diǎn)。這BMP中的2048個(gè)碼點(diǎn)對(duì)于BMP總計(jì)65536個(gè)碼點(diǎn)來說,僅占3.125%(2048/65536=0.03125)。

在UTF-16編碼方式中,引導(dǎo)代理的后面應(yīng)該是一個(gè)尾隨代理,而尾隨代理的前面就應(yīng)該是一個(gè)引導(dǎo)代理;不能出現(xiàn)一個(gè)引導(dǎo)代理的后面是一個(gè)非代理的普通UTF-16碼元的情況,也不能出現(xiàn)一個(gè)引導(dǎo)代理的后面還是一個(gè)引導(dǎo)代理的情況。
UTF-16文本(字符串)的最后一個(gè)碼元不能是引導(dǎo)代理,不允許出現(xiàn)一個(gè)尾隨代理的前面是一個(gè)尾隨代理的情況,也不允許出現(xiàn)一個(gè)尾隨代理的前面是一個(gè)非代理的普通UTF-16碼元的情況;UTF-16文本(字符串)的第一個(gè)碼元不能是尾隨代理。
而單獨(dú)的一個(gè)代理碼元(不管是引導(dǎo)代理還是尾隨代理)是不合法的,代理必須以一個(gè)“引導(dǎo)代理+尾隨代理”編碼對(duì)(即代理對(duì))的形式出現(xiàn)。
UTF-16的這種“代理對(duì)”編碼規(guī)則保證了文本處理程序能夠正確地訪問和處理包括了基本平面和增補(bǔ)平面在內(nèi)的全部UTF-16碼元序列,并消除了基本平面字符和增補(bǔ)平面字符之間發(fā)生沖突的可能性。
因?yàn)橐龑?dǎo)代理和尾隨代理碼元被各自規(guī)定在一個(gè)特定范圍內(nèi)取值,所以很簡(jiǎn)單的一個(gè)原則就是:凡是在代理編碼范圍內(nèi)的碼元就是“代理”增補(bǔ)平面SP字符的“代理碼元”,否則就是“基本平面BMP字符的碼元”。由于BMP中的字符碼元和代理碼元分別在各自獨(dú)立的編碼范圍內(nèi)進(jìn)行編碼,所以對(duì)于一個(gè)符合格式規(guī)范的UTF-16碼元來講,它必須滿足以下三個(gè)條件之一:
-非代理碼元(BMP字符碼元)必須避開代理碼元所占用的范圍0xD800~0xDFFF(二進(jìn)制為1101 1000 0000 0000 ~ 1101 1111 1111 1111,共2048個(gè));
-引導(dǎo)代理必須是代理對(duì)中的第一個(gè)碼元;
-尾隨代理必須是代理對(duì)中的第二個(gè)碼元。
在處理UTF-16文本時(shí),為了確保文本數(shù)據(jù)的完整性,絕對(duì)不能把任意一個(gè)代理從代理對(duì)中拆出來,也不能在代理對(duì)中間插入另一個(gè)字符的碼元或碼元序列。
在UTF-16編碼方式里面,一個(gè)Unicode字符碼點(diǎn)值由一個(gè)或兩個(gè)16位碼元編碼。所以,如果想在一個(gè)UTF-16碼元序列里面判斷某個(gè)碼元是屬于哪個(gè)字符的話,就需要檢查那個(gè)碼元的值,然后根據(jù)碼元的類型(是否具有代理標(biāo)志位)決定是否還需要向前或向后檢查一個(gè)相鄰的碼元的值(可以不必理會(huì)除了前后相鄰的兩個(gè)碼元之外的其他碼元)。
由于引導(dǎo)代理、尾隨代理、BMP字符碼元,三者互不重疊,搜索就很簡(jiǎn)單,這意味著UTF-16具有“自同步”(self-synchronizing)性:通過僅檢查一個(gè)碼元就可以判斷當(dāng)前字符的下一個(gè)字符的起始碼元,每個(gè)字符碼元的邊界很明確;同時(shí),還具有“非傳遞”性:?jiǎn)为?dú)的一個(gè)UTF-16碼元出錯(cuò)涉及的只是一個(gè)字符,不會(huì)傳遞到文本的其他部分去,因此,即使文本中某些字符數(shù)據(jù)遭到破壞,其影響也只是局部性的。
UTF-8也有類似優(yōu)點(diǎn)。但許多早期的編碼方式就不是自同步的,比如大多數(shù)的多字節(jié)編碼標(biāo)準(zhǔn)如GBK、Big5等,必須從頭開始分析文本才能確定不同字符的碼元的邊界;也不具有非傳遞性,局部字符數(shù)據(jù)被破壞,很可能傳遞到整個(gè)文件,導(dǎo)致整個(gè)文件無法正確顯示。
因此,UTF-8和UTF-16編碼方式所具有的“自同步性”、“非傳遞性”等特點(diǎn)除了增強(qiáng)抗干擾能力外,也提供了隨機(jī)訪問的能力。
由于在大多數(shù)的文本數(shù)據(jù)中,代理對(duì)(增補(bǔ)字符碼元序列)出現(xiàn)的概率是很小的,很多情況下處理的還是非代理碼元(即BMP字符碼元),導(dǎo)致許多軟件處理代理對(duì)的部分往往得不到充分的測(cè)試。這導(dǎo)致了一些長(zhǎng)期的bug與潛在安全漏洞,甚至廣為流行、得到良好評(píng)價(jià)的優(yōu)秀軟件也是如此。
因此,雖然編程時(shí)同時(shí)考慮文本中可能出現(xiàn)的不同存儲(chǔ)長(zhǎng)度的字符(BMP有效字符是單16位編碼,即單碼元編碼;增補(bǔ)字符是雙16位編碼,即雙碼元編碼)并相應(yīng)做出不同的處理,會(huì)比單純只考慮16位編碼在性能上要遜色一些。但實(shí)際上,現(xiàn)有的遵循定長(zhǎng)16位編碼規(guī)范但不能處理代理對(duì)的程序只需做很小的一點(diǎn)修改就可以同時(shí)處理BMP有效字符和增補(bǔ)字符的編碼了。
另外,需要特別注意的是,雖然Unicode標(biāo)準(zhǔn)規(guī)定BMP代理區(qū)(U+D800~,lll.,lllllllU+DFFF)的碼點(diǎn)值不對(duì)應(yīng)于任何字符,即未作定義,但是在使用UCS-2的時(shí)代,U+D800~U+DFFF是被定義了的,也就是已經(jīng)用于某些字符了。不過,只要不是恰好構(gòu)成了代理對(duì),許多程序還是能把這些不匹配Unicode標(biāo)準(zhǔn)的字符碼元正確地辨識(shí)、轉(zhuǎn)換成合規(guī)的碼元。這種由歷史原因造成的碼元序列按現(xiàn)在的Unicode標(biāo)準(zhǔn)來看,應(yīng)算作是編碼錯(cuò)誤。