字符集簡史
在所有字符集中,最知名可能
要數被稱為ASCII的7位字符集了。它是美國信息交換標準委員會
(American?Standards?Committee?for?Information?Interchange)的縮寫,?為美國英語通信所設
計。它由128個字符組成,包括大小寫字母、數字0-9、標點符號、非打印字符(換行符、制表符等4個)以及控制字符(退格、響鈴等)組成。
但
是,由于他是針對英語設計的,當處理帶有音調標號(形如漢語的拼音)的歐洲文字時就會出現問題。因此,創建出了一些包括255個字符的由ASCII擴展的
字符集。其中有一種通常被成為IBM字符集,它把值為128-255之間的字符用于畫圖和畫線,以及一些特殊的歐洲字符。另一種8位字符集是
ISO?8859-1?Latin?1,也簡稱為ISO?Latin-1。它把位于128-255之間的字符用于拉丁字母表中特殊語言字符的編碼,也因此
而得名。
歐洲語言不是地球上的唯一語言,因此亞洲和非洲語言并不能被8位字符
集所支持。僅漢語(或pictograms)字母表就有80000以上個字符。但是把漢語、日語和越南語的一些相似的字符結合起來,在不同的語言里,使不
同的字符代表不同的字,這樣只用2個字節就可以編碼地球上幾乎所有地區的文字。因此,創建了UNICODE編碼。它通過增加一個高字節對
ISO?Latin-1字符集進行擴展,當這些高字節位為0時,低字節就是ISO?Latin-1字符。UNICODE支持歐洲、非洲、中東、亞洲(包括
統一標準的東亞像形漢字和韓國像形文字)。但是,UNICODE并沒有提供對諸如Braille,?Cherokee,?Ethiopic,
?Khmer,?Mongolian,?Hmong,?Tai?Lu,?Tai?Mau文字的支持。同時它也不支持如Ahom,?Akkadian,
?Aramaic,?Babylonian?Cuneiform,?Balti,?Brahmi,?Etruscan,?Hittite,?Javanese,
?Numidian,?Old?Persian?Cuneiform,?Syrian之類的古老的文字。
事
實證明,對可以用ASCII表示的字符使用UNICODE并不高效,因為UNICODE比ASCII占用大一倍的空間,而對ASCII來說高字節的0對他
毫無用處。為了解決這個問題,就出現了一些中間格式的字符集,他們被稱為通用轉換格式,既UTF
(Universal?Transformation?Format)。目前存在的UTF格式有:UTF-7,?UTF-7.5,?UTF-8,?UTF
-16,?以及?UTF-32。本文討論UTF-8字符集的基礎。
UTF_8字符集
UTF
-8是UNICODE的一種變長字符編碼,由Ken?Thompson于1992年創建。現在已經標準化為RFC?3629。UTF-8用1到6個字節編
碼UNICODE字符。如果UNICODE字符由2個字節表示,則編碼成UTF-8很可能需要3個字節,而如果UNICODE字符由4個字節表示,則編碼
成UTF-8可能需要6個字節。用4個或6個字節去編碼一個UNICODE字符可能太多了,但很少會遇到那樣的UNICODE字符。
UFT-8轉換表表示如下:
UNICODE?UTF-8?
00000000?-?0000007F?0xxxxxxx?
00000080?-?000007FF?110xxxxx?10xxxxxx?
00000800?-?0000FFFF?1110xxxx?10xxxxxx?10xxxxxx?
00010000?-?001FFFFF?11110xxx?10xxxxxx?10xxxxxx?10xxxxxx?
00200000?-?03FFFFFF?111110xx?10xxxxxx?10xxxxxx?10xxxxxx?10xxxxxx?
04000000?-?7FFFFFFF?1111110x?10xxxxxx?10xxxxxx?10xxxxxx?10xxxxxx?10xxxxxx?
實
際表示ASCII字符的UNICODE字符,將會編碼成1個字節,并且UTF-8表示與ASCII字符表示是一樣的。所有其他的UNCODE字符轉化成
UTF-8將需要至少2個字節。每個字節由一個換碼序列開始。第一個字節由唯一的換碼序列,由n位1加一位0組成。n位1表示字符編碼所需的字節數。
示例
UNICODE?uCA(11001010)?編碼成UTF-8將需要2個字節:
uCA?->?C3?8A
1100?1010
110xxxxx?10xxxxxx
1100?1010?->?110xxxxx?10xxxxxx
->?110xxxxx?10xxxxx0
->?110xxxxx?10xxxx10
->?110xxxxx?10xxx010
->?110xxxxx?10xx1010
->?110xxxxx?10x01010
->?110xxxxx?10001010
->?110xxxx1?10001010
->?110xxx11?10001010
->?11000011?10001010
->?C3?8A
UNICODE?uF03F?(11110000?00111111)?編碼成UTF-8將需要3個字節:
u?F03F?->?EF?80?BF
1111?0000?0011?1111?->?1110xxxx?10xxxxxx?10xxxxxx
->?11101111?10000000?10111111
->?EF?80?BF
譯者注:由上分析可以看到,UNCODE到UTF-8的轉換就是先確定編碼所需要的字節數,然后用UNICODE編碼位從低位到高位依次填入上面表示為x的位上,不足的高位以0補充。以上是個人經驗,如有錯誤,請不惜指教,謝過先:)
UTF-8編碼的優點:
UTF-8編碼可以通過屏蔽位和移位操作快速讀寫。
字符串比較時strcmp()和wcscmp()的返回結果相同,因此使排序變得更加容易。
字節FF和FE在UTF-8編碼中永遠不會出現,因此他們可以用來表明UTF-16或UTF-32文本(見BOM)
UTF-8?是字節順序無關的。它的字節順序在所有系統中都是一樣的,因此它實際上并不需要BOM。
UTF-8編碼的缺點:
你無法從UNICODE字符數判斷出UTF-8文本的字節數,因為UTF-8是一種變長編碼
它需要用2個字節編碼那些用擴展ASCII字符集只需1個字節的字符
ISO?Latin-1?是UNICODE的子集,但不是UTF-8的子集
8位字符的UTF-8編碼會被email網關過濾,因為internet信息最初設計為7為ASCII碼。因此產生了UTF-7編碼。
UTF-8?在它的表示中使用值100xxxxx的幾率超過50%,?而現存的實現如ISO?2022,?4873,?6429,?和8859系統,會把它錯認為是C1?控制碼。因此產生了UTF-7.5編碼。
修正的UTF-8:
java使用UTF-16表示內部文本,并支持用于字符串串行化的非標準的修正UTF-8編碼。標準UTF-8和修正的UTF-8有兩點不同:
修
正的UTF-8中,null字符編碼成2個字節(11000000?00000000)?而不是標準的1個字節(00000000),這樣作可以保證編碼
后的字符串中不會嵌入null字符。因此如果在類C語言中處理字符串,文本不會在第一個null字符時截斷(C字符串以null結尾)。
在標準
UTF-8編碼中,超出基本多語言范圍(BMP?-?Basic?Multilingual?Plain)的字符被編碼為4字節格式,但是在修正的UTF
-8編碼中,他們由代理編碼對(surrogate?pairs)表示,然后這些代理編碼對在序列中分別重新編碼。結果標準UTF-8編碼中需要4個字節
的字符,在修正后的UTF-8編碼中將需要6個字節。
位序標志BOM
BOM(Byte?Order?Mark)是一個字符,它表明UNICODE文本的UTF-16,UTF-32的編碼字節順序(高字節低字節順序)和編碼方式(UTF-8,UTF-16,UTF-32,?其中UTF-8編碼是字節順序無關的)。
如下所示:
Encoding?Representation?
UTF-8?EF?BB?BF?
UTF-16?Big?Endian?FE?FF?
UTF-16?Little?Endian?FF?FE?
UTF-32?Big?Endian?00?00?FE?FF
UTF-32?Little?Endian?FF?FE?00?00
UTF-8?C++?程序編碼示例:
下面是四個C++函數,他們分別實現2字節和4字節UNICODE和UTF-8之間的轉換。
#define?MASKBITS?0x3F
#define?MASKBYTE?0x80
#define?MASK2BYTES?0xC0
#define?MASK3BYTES?0xE0
#define?MASK4BYTES?0xF0
#define?MASK5BYTES?0xF8
#define?MASK6BYTES?0xFC
typedef?unsigned?short?Unicode2Bytes;
typedef?unsigned?int?Unicode4Bytes;
void?UTF8Encode2BytesUnicode(std::vector<?Unicode2Bytes?>?input,
std::vector<?byte?>&?output)
{
for(int?i=0;?i?<?input.size();?i++)
{
//?0xxxxxxx
if(input?<?0x80)
{
output.push_back((byte)input);
}
//?110xxxxx?10xxxxxx
else?if(input?<?0x800)
{
output.push_back((byte)(MASK2BYTES?|?input?>>?6));
output.push_back((byte)(MASKBYTE?|?input?&?MASKBITS));
}
//?1110xxxx?10xxxxxx?10xxxxxx
else?if(input?<?0x10000)
{
output.push_back((byte)(MASK3BYTES?|?input?>>?12));
output.push_back((byte)(MASKBYTE?|?input?>>?6?&?MASKBITS));
output.push_back((byte)(MASKBYTE?|?input?&?MASKBITS));
}
}
}
void?UTF8Decode2BytesUnicode(std::vector<?byte?>?input,
std::vector<?Unicode2Bytes?>&?output)
{
for(int?i=0;?i?<?input.size();)
{
Unicode2Bytes?ch;
//?1110xxxx?10xxxxxx?10xxxxxx
if((input?&?MASK3BYTES)?==?MASK3BYTES)
{
ch?=?((input?&?0x0F)?<<?12)?|?(
(input[i+1]?&?MASKBITS)?<<?6)
|?(input[i+2]?&?MASKBITS);
i?+=?3;
}
//?110xxxxx?10xxxxxx
else?if((input?&?MASK2BYTES)?==?MASK2BYTES)
{
ch?=?((input?&?0x1F)?<<?6)?|?(input[i+1]?&?MASKBITS);
i?+=?2;
}
//?0xxxxxxx
else?if(input?<?MASKBYTE)
{
ch?=?input;
i?+=?1;
}
output.push_back(ch);
}
}
void?UTF8Encode4BytesUnicode(std::vector<?Unicode4Bytes?>?input,
std::vector<?byte?>&?output)
{
for(int?i=0;?i?<?input.size();?i++)
{
//?0xxxxxxx
if(input?<?0x80)
{
output.push_back((byte)input);
}
//?110xxxxx?10xxxxxx
else?if(input?<?0x800)
{
output.push_back((byte)(MASK2BYTES?|?input?>?6));
output.push_back((byte)(MASKBYTE?|?input?&?MASKBITS));
}
//?1110xxxx?10xxxxxx?10xxxxxx
else?if(input?<?0x10000)
{
output.push_back((byte)(MASK3BYTES?|?input?>>?12));
output.push_back((byte)(MASKBYTE?|?input?>>?6?&?MASKBITS));
output.push_back((byte)(MASKBYTE?|?input?&?MASKBITS));
}
//?11110xxx?10xxxxxx?10xxxxxx?10xxxxxx
else?if(input?<?0x200000)
{
output.push_back((byte)(MASK4BYTES?|?input?>>?18));
output.push_back((byte)(MASKBYTE?|?input?>>?12?&?MASKBITS));
output.push_back((byte)(MASKBYTE?|?input?>>?6?&?MASKBITS));
output.push_back((byte)(MASKBYTE?|?input?&?MASKBITS));
}
//?111110xx?10xxxxxx?10xxxxxx?10xxxxxx?10xxxxxx
else?if(input?<?0x4000000)
{
output.push_back((byte)(MASK5BYTES?|?input?>>?24));
output.push_back((byte)(MASKBYTE?|?input?>>?18?&?MASKBITS));
output.push_back((byte)(MASKBYTE?|?input?>>?12?&?MASKBITS));
output.push_back((byte)(MASKBYTE?|?input?>>?6?&?MASKBITS));
output.push_back((byte)(MASKBYTE?|?input?&?MASKBITS));
}
//?1111110x?10xxxxxx?10xxxxxx?10xxxxxx?10xxxxxx?10xxxxxx
else?if(input?<?0x8000000)
{
output.push_back((byte)(MASK6BYTES?|?input?>>?30));
output.push_back((byte)(MASKBYTE?|?input?>>?18?&?MASKBITS));
output.push_back((byte)(MASKBYTE?|?input?>>?12?&?MASKBITS));
output.push_back((byte)(MASKBYTE?|?input?>>?6?&?MASKBITS));
output.push_back((byte)(MASKBYTE?|?input?&?MASKBITS));
}
}
}
void?UTF8Decode4BytesUnicode(std::vector<?byte?>?input,
std::vector<?Unicode4Bytes?>&?output)
{
for(int?i=0;?i?<?input.size();)
{
Unicode4Bytes?ch;
//?1111110x?10xxxxxx?10xxxxxx?10xxxxxx?10xxxxxx?10xxxxxx
if((input?&?MASK6BYTES)?==?MASK6BYTES)
{
ch?=?((input?&?0x01)?<<?30)?|?((input[i+1]?&?MASKBITS)?<<?24)
|?((input[i+2]?&?MASKBITS)?<<?18)?|?((input[i+3]
&?MASKBITS)?<<?12)
|?((input[i+4]?&?MASKBITS)?<<?6)?|?(input[i+5]?&?MASKBITS);
i?+=?6;
}
//?111110xx?10xxxxxx?10xxxxxx?10xxxxxx?10xxxxxx
else?if((input?&?MASK5BYTES)?==?MASK5BYTES)
{
ch?=?((input?&?0x03)?<<?24)?|?((input[i+1]
&?MASKBITS)?<<?18)
|?((input[i+2]?&?MASKBITS)?<<?12)?|?((input[i+3]
&?MASKBITS)?<<?6)
|?(input[i+4]?&?MASKBITS);
i?+=?5;
}
//?11110xxx?10xxxxxx?10xxxxxx?10xxxxxx
else?if((input?&?MASK4BYTES)?==?MASK4BYTES)
{
ch?=?((input?&?0x07)?<<?18)?|?((input[i+1]
&?MASKBITS)?<<?12)
|?((input[i+2]?&?MASKBITS)?<<?6)?|?(input[i+3]?&?MASKBITS);
i?+=?4;
}
//?1110xxxx?10xxxxxx?10xxxxxx
else?if((input?&?MASK3BYTES)?==?MASK3BYTES)
{
ch?=?((input?&?0x0F)?<<?12)?|?((input[i+1]?&?MASKBITS)?<<?6)
|?(input[i+2]?&?MASKBITS);
i?+=?3;
}
//?110xxxxx?10xxxxxx
else?if((input?&?MASK2BYTES)?==?MASK2BYTES)
{
ch?=?((input?&?0x1F)?<<?6)?|?(input[i+1]?&?MASKBITS);
i?+=?2;
}
//?0xxxxxxx
else?if(input?<?MASKBYTE)
{
ch?=?input;
i?+=?1;
}
output.push_back(ch);
}
}
限譯者水平有限,有不解之處請參考原文。版權屬原文作者所有,轉載請注明出處及作者。
原文參見:http://www.codeguru.com/Cpp/misc ... article.php/c10451/