隨筆-128  評論-55  文章-5  trackbacks-0

           談?wù)刄nicode編碼,簡要解釋UCS、UTF、BMP、BOM等名詞

          這是一篇程序員寫給程序員的趣味讀物。所謂趣味是指可以比較輕松地了解一些原來不清楚的概念,增進(jìn)知識(shí),類似于打RPG游戲的升級(jí)。整理這篇文章的動(dòng)機(jī)是兩個(gè)問題:

          問題一:

          使用Windows記事本的“另存為”,可以在GBK、Unicode、Unicode big endian和UTF-8這幾種編碼方式間相互轉(zhuǎn)換。同樣是txt文件,Windows是怎樣識(shí)別編碼方式的呢?

          我很早前就發(fā)現(xiàn)Unicode、Unicode big endian和UTF-8編碼的txt文件的開頭會(huì)多出幾個(gè)字節(jié),分別是FF、FE(Unicode),FE、FF(Unicode big endian),EF、BB、BF(UTF-8)。但這些標(biāo)記是基于什么標(biāo)準(zhǔn)呢?

          問題二:
          最近在網(wǎng)上看到一個(gè)ConvertUTF.c,實(shí)現(xiàn)了UTF-32、UTF-16和UTF-8這三種編碼方式的相互轉(zhuǎn)換。對于Unicode(UCS2)、GBK、UTF-8這些編碼方式,我原來就了解。但這個(gè)程序讓我有些糊涂,想不起來UTF-16和UCS2有什么關(guān)系。

          查了查相關(guān)資料,總算將這些問題弄清楚了,順帶也了解了一些Unicode的細(xì)節(jié)。寫成一篇文章,送給有過類似疑問的朋友。本文在寫作時(shí)盡量做到通俗易懂,但要求讀者知道什么是字節(jié),什么是十六進(jìn)制。

          0、big endian和little endian

          big endian和little endian是CPU處理多字節(jié)數(shù)的不同方式。例如“漢”字的Unicode編碼是6C49。那么寫到文件里時(shí),究竟是將6C寫在前面,還是將49寫在前面?如果將6C寫在前面,就是big endian。如果將49寫在前面,就是little endian。

          “endian”這個(gè)詞出自《格列佛游記》。小人國的內(nèi)戰(zhàn)就源于吃雞蛋時(shí)是究竟從大頭(Big-Endian)敲開還是從小頭(Little-Endian)敲開,由此曾發(fā)生過六次叛亂,一個(gè)皇帝送了命,另一個(gè)丟了王位。

          我們一般將endian翻譯成“字節(jié)序”,將big endian和little endian稱作“大尾”和“小尾”。

          1、字符編碼、內(nèi)碼,順帶介紹漢字編碼

          字符必須編碼后才能被計(jì)算機(jī)處理。計(jì)算機(jī)使用的缺省編碼方式就是計(jì)算機(jī)的內(nèi)碼。早期的計(jì)算機(jī)使用7位的ASCII編碼,為了處理漢字,程序員設(shè)計(jì)了用于簡體中文的GB2312和用于繁體中文的big5。

          GB2312(1980年)一共收錄了7445個(gè)字符,包括6763個(gè)漢字和682個(gè)其它符號(hào)。漢字區(qū)的內(nèi)碼范圍高字節(jié)從B0-F7,低字節(jié)從A1-FE,占用的碼位是72*94=6768。其中有5個(gè)空位是D7FA-D7FE。

          GB2312支持的漢字太少。1995年的漢字?jǐn)U展規(guī)范GBK1.0收錄了21886個(gè)符號(hào),它分為漢字區(qū)和圖形符號(hào)區(qū)。漢字區(qū)包括21003個(gè)字符。

          從ASCII、GB2312到GBK,這些編碼方法是向下兼容的,即同一個(gè)字符在這些方案中總是有相同的編碼,后面的標(biāo)準(zhǔn)支持更多的字符。在這些編碼中,英文和中文可以統(tǒng)一地處理。區(qū)分中文編碼的方法是高字節(jié)的最高位不為0。按照程序員的稱呼,GB2312、GBK都屬于雙字節(jié)字符集 (DBCS)。

          2000年的GB18030是取代GBK1.0的正式國家標(biāo)準(zhǔn)。該標(biāo)準(zhǔn)收錄了27484個(gè)漢字,同時(shí)還收錄了藏文、蒙文、維吾爾文等主要的少數(shù)民族文字。從漢字字匯上說,GB18030在GB13000.1的20902個(gè)漢字的基礎(chǔ)上增加了CJK擴(kuò)展A的6582個(gè)漢字(Unicode碼0x3400-0x4db5),一共收錄了27484個(gè)漢字。

          CJK就是中日韓的意思。Unicode為了節(jié)省碼位,將中日韓三國語言中的文字統(tǒng)一編碼。GB13000.1就是ISO/IEC 10646-1的中文版,相當(dāng)于Unicode 1.1。

          GB18030的編碼采用單字節(jié)、雙字節(jié)和4字節(jié)方案。其中單字節(jié)、雙字節(jié)和GBK是完全兼容的。4字節(jié)編碼的碼位就是收錄了CJK擴(kuò)展A的6582個(gè)漢字。 例如:UCS的0x3400在GB18030中的編碼應(yīng)該是8139EF30,UCS的0x3401在GB18030中的編碼應(yīng)該是8139EF31。

          微軟提供了GB18030的升級(jí)包,但這個(gè)升級(jí)包只是提供了一套支持CJK擴(kuò)展A的6582個(gè)漢字的新字體:新宋體-18030,并不改變內(nèi)碼。Windows 的內(nèi)碼仍然是GBK。

          這里還有一些細(xì)節(jié):

          • GB2312的原文還是區(qū)位碼,從區(qū)位碼到內(nèi)碼,需要在高字節(jié)和低字節(jié)上分別加上A0。

          • 對于任何字符編碼,編碼單元的順序是由編碼方案指定的,與endian無關(guān)。例如GBK的編碼單元是字節(jié),用兩個(gè)字節(jié)表示一個(gè)漢字。 這兩個(gè)字節(jié)的順序是固定的,不受CPU字節(jié)序的影響。UTF-16的編碼單元是word(雙字節(jié)),word之間的順序是編碼方案指定的,word內(nèi)部的字節(jié)排列才會(huì)受到endian的影響。后面還會(huì)介紹UTF-16。

          • GB2312的兩個(gè)字節(jié)的最高位都是1。但符合這個(gè)條件的碼位只有128*128=16384個(gè)。所以GBK和GB18030的低字節(jié)最高位都可能不是1。不過這不影響DBCS字符流的解析:在讀取DBCS字符流時(shí),只要遇到高位為1的字節(jié),就可以將下兩個(gè)字節(jié)作為一個(gè)雙字節(jié)編碼,而不用管低字節(jié)的高位是什么。

          2、Unicode、UCS和UTF

          前面提到從ASCII、GB2312、GBK到GB18030的編碼方法是向下兼容的。而Unicode只與ASCII兼容(更準(zhǔn)確地說,是與ISO-8859-1兼容),與GB碼不兼容。例如“漢”字的Unicode編碼是6C49,而GB碼是BABA。

          Unicode也是一種字符編碼方法,不過它是由國際組織設(shè)計(jì),可以容納全世界所有語言文字的編碼方案。Unicode的學(xué)名是"Universal Multiple-Octet Coded Character Set",簡稱為UCS。UCS可以看作是"Unicode Character Set"的縮寫。

          根據(jù)維基百科全書(http://zh.wikipedia.org/wiki/)的記載:歷史上存在兩個(gè)試圖獨(dú)立設(shè)計(jì)Unicode的組織,即國際標(biāo)準(zhǔn)化組織(ISO)和一個(gè)軟件制造商的協(xié)會(huì)(unicode.org)。ISO開發(fā)了ISO 10646項(xiàng)目,Unicode協(xié)會(huì)開發(fā)了Unicode項(xiàng)目。

          在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)是ISO 10646-3:2003。

          UCS只是規(guī)定如何編碼,并沒有規(guī)定如何傳輸、保存這個(gè)編碼。例如“漢”字的UCS編碼是6C49,我可以用4個(gè)ascii數(shù)字來傳輸、保存這個(gè)編碼;也可以用utf-8編碼:3個(gè)連續(xù)的字節(jié)E6 B1 89來表示它。關(guān)鍵在于通信雙方都要認(rèn)可。UTF-8、UTF-7、UTF-16都是被廣泛接受的方案。UTF-8的一個(gè)特別的好處是它與ISO-8859-1完全兼容。UTF是“UCS Transformation Format”的縮寫。

          IETF的RFC2781和RFC3629以RFC的一貫風(fēng)格,清晰、明快又不失嚴(yán)謹(jǐn)?shù)孛枋隽薝TF-16和UTF-8的編碼方法。我總是記不得IETF是Internet Engineering Task Force的縮寫。但I(xiàn)ETF負(fù)責(zé)維護(hù)的RFC是Internet上一切規(guī)范的基礎(chǔ)。

          2.1、內(nèi)碼和code page

          目前Windows的內(nèi)核已經(jīng)支持Unicode字符集,這樣在內(nèi)核上可以支持全世界所有的語言文字。但是由于現(xiàn)有的大量程序和文檔都采用了某種特定語言的編碼,例如GBK,Windows不可能不支持現(xiàn)有的編碼,而全部改用Unicode。

          Windows使用代碼頁(code page)來適應(yīng)各個(gè)國家和地區(qū)。code page可以被理解為前面提到的內(nèi)碼。GBK對應(yīng)的code page是CP936。

          微軟也為GB18030定義了code page:CP54936。但是由于GB18030有一部分4字節(jié)編碼,而Windows的代碼頁只支持單字節(jié)和雙字節(jié)編碼,所以這個(gè)code page是無法真正使用的。

          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位為單元對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。

          讀者可以用記事本測試一下我們的編碼是否正確。需要注意,UltraEdit在打開utf-8編碼的文本文件時(shí)會(huì)自動(dòng)轉(zhuǎn)換為UTF-16,可能產(chǎn)生混淆。你可以在設(shè)置中關(guān)掉這個(gè)選項(xiàng)。更好的工具是Hex Workshop。

          UTF-16以16位為單元對UCS進(jìn)行編碼。對于小于0x10000的UCS碼,UTF-16編碼就等于UCS碼對應(yīng)的16位無符號(hào)整數(shù)。對于不小于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é)序。例如“奎”的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)槲议_始的疑問都找到了答案,所以就沒有看:

          1. "Understanding Unicode A general introduction to the Unicode Standard" (http://scripts.sil.org/cms/scripts/page.php?site_id=nrsi&item_id=IWS-Chapter04a)
          2. "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。希望有讀者能從中受益。

          附錄1 再說說區(qū)位碼、GB2312、內(nèi)碼和代碼頁

          有的朋友對文章中這句話還有疑問:
          “GB2312的原文還是區(qū)位碼,從區(qū)位碼到內(nèi)碼,需要在高字節(jié)和低字節(jié)上分別加上A0。”

          我再詳細(xì)解釋一下:

          “GB2312的原文”是指國家1980年的一個(gè)標(biāo)準(zhǔn)《中華人民共和國國家標(biāo)準(zhǔn) 信息交換用漢字編碼字符集 基本集 GB 2312-80》。這個(gè)標(biāo)準(zhǔn)用兩個(gè)數(shù)來編碼漢字和中文符號(hào)。第一個(gè)數(shù)稱為“區(qū)”,第二個(gè)數(shù)稱為“位”。所以也稱為區(qū)位碼。1-9區(qū)是中文符號(hào),16-55區(qū)是一級(jí)漢字,56-87區(qū)是二級(jí)漢字。現(xiàn)在Windows也還有區(qū)位輸入法,例如輸入1601得到“啊”。(這個(gè)區(qū)位輸入法可以自動(dòng)識(shí)別16進(jìn)制的GB2312和10進(jìn)制的區(qū)位碼,也就是說輸入B0A1同樣會(huì)得到“啊”。)

          內(nèi)碼是指操作系統(tǒng)內(nèi)部的字符編碼。早期操作系統(tǒng)的內(nèi)碼是與語言相關(guān)的。現(xiàn)在的Windows在系統(tǒng)內(nèi)部支持Unicode,然后用代碼頁適應(yīng)各種語言,“內(nèi)碼”的概念就比較模糊了。微軟一般將缺省代碼頁指定的編碼說成是內(nèi)碼。

          內(nèi)碼這個(gè)詞匯,并沒有什么官方的定義,代碼頁也只是微軟這個(gè)公司的叫法。作為程序員,我們只要知道它們是什么東西,沒有必要過多地考證這些名詞。

          Windows中有缺省代碼頁的概念,即缺省用什么編碼來解釋字符。例如Windows的記事本打開了一個(gè)文本文件,里面的內(nèi)容是字節(jié)流:BA、BA、D7、D6。Windows應(yīng)該去怎么解釋它呢?

          是按照Unicode編碼解釋、還是按照GBK解釋、還是按照BIG5解釋,還是按照ISO8859-1去解釋?如果按GBK去解釋,就會(huì)得到“漢字”兩個(gè)字。按照其它編碼解釋,可能找不到對應(yīng)的字符,也可能找到錯(cuò)誤的字符。所謂“錯(cuò)誤”是指與文本作者的本意不符,這時(shí)就產(chǎn)生了亂碼。

          答案是Windows按照當(dāng)前的缺省代碼頁去解釋文本文件里的字節(jié)流。缺省代碼頁可以通過控制面板的區(qū)域選項(xiàng)設(shè)置。記事本的另存為中有一項(xiàng)ANSI,其實(shí)就是按照缺省代碼頁的編碼方法保存。

          Windows的內(nèi)碼是Unicode,它在技術(shù)上可以同時(shí)支持多個(gè)代碼頁。只要文件能說明自己使用什么編碼,用戶又安裝了對應(yīng)的代碼頁,Windows就能正確顯示,例如在HTML文件中就可以指定charset。

          有的HTML文件作者,特別是英文作者,認(rèn)為世界上所有人都使用英文,在文件中不指定charset。如果他使用了0x80-0xff之間的字符,中文Windows又按照缺省的GBK去解釋,就會(huì)出現(xiàn)亂碼。這時(shí)只要在這個(gè)html文件中加上指定charset的語句,例如:
          <meta http-equiv="Content-Type" content="text/html; charset=ISO8859-1">
          如果原作者使用的代碼頁和ISO8859-1兼容,就不會(huì)出現(xiàn)亂碼了。

          再說區(qū)位碼,啊的區(qū)位碼是1601,寫成16進(jìn)制是0x10,0x01。這和計(jì)算機(jī)廣泛使用的ASCII編碼沖突。為了兼容00-7f的ASCII編碼,我們在區(qū)位碼的高、低字節(jié)上分別加上A0。這樣“啊”的編碼就成為B0A1。我們將加過兩個(gè)A0的編碼也稱為GB2312編碼,雖然GB2312的原文根本沒提到這一點(diǎn)。

          文章二,本文轉(zhuǎn)載自:http://www.donews.net/holen/archive/2004/11/30/188182.aspx

          Unicode:

          unicode.org制定的編碼機(jī)制, 要將全世界常用文字都函括進(jìn)去.
          在1.0中是16位編碼, 由U+0000到U+FFFF. 每個(gè)2byte碼對應(yīng)一個(gè)字符; 在2.0開始拋棄了16位限制, 原來的16位作為基本位平面, 另外增加了16個(gè)位平面, 相當(dāng)于20位編碼, 編碼范圍0到0x10FFFF.

          UCS:

          ISO制定的ISO10646標(biāo)準(zhǔn)所定義的 Universal Character Set, 采用4byte編碼.

          Unicode與UCS的關(guān)系:

          ISO與unicode.org是兩個(gè)不同的組織, 因此最初制定了不同的標(biāo)準(zhǔn); 但自從unicode2.0開始, unicode采用了與ISO 10646-1相同的字庫和字碼, ISO也承諾ISO10646將不會(huì)給超出0x10FFFF的UCS-4編碼賦值, 使得兩者保持一致.

          UCS的編碼方式:

        1. UCS-2, 與unicode的2byte編碼基本一樣.
        2. UCS-4, 4byte編碼, 目前是在UCS-2前加上2個(gè)全零的byte.

          UTF: Unicode/UCS Transformation Format
          ---------------------------------------原文------------------------------ 
        3. UTF-8, 8bit編碼, ASCII不作變換, 其他字符做變長編碼, 每個(gè)字符1-3 byte. 通常作為外碼. 有以下優(yōu)點(diǎn):
          * 與CPU字節(jié)順序無關(guān), 可以在不同平臺(tái)之間交流
          * 容錯(cuò)能力高, 任何一個(gè)字節(jié)損壞后, 最多只會(huì)導(dǎo)致一個(gè)編碼碼位損失, 不會(huì)鏈鎖錯(cuò)誤(如GB碼錯(cuò)一個(gè)字節(jié)就會(huì)整行亂碼)
        4. UTF-16, 16bit編碼, 是變長碼, 大致相當(dāng)于20位編碼, 值在0到0x10FFFF之間, 基本上就是unicode編碼的實(shí)現(xiàn). 它是變長碼, 與CPU字序有關(guān), 但因?yàn)樽钍】臻g, 常作為網(wǎng)絡(luò)傳輸?shù)耐獯a.

          ---------------------------------------原文------------------------------ 
          ---------------------------------------糾正后----------------------------

        5. UTF-8, 8bit編碼, ASCII不作變換, 其他字符做變長編碼, 每個(gè)字符1-3 byte. 通常作為外碼. 有以下優(yōu)點(diǎn):
          * 與CPU字節(jié)順序無關(guān), 可以在不同平臺(tái)之間交流
          * 容錯(cuò)能力高, 任何一個(gè)字節(jié)損壞后, 最多只會(huì)導(dǎo)致一個(gè)編碼碼位損失, 不會(huì)鏈鎖錯(cuò)誤(如GB碼錯(cuò)一個(gè)字節(jié)就會(huì)整行亂碼)
        6. UTF-16, 16bit編碼, 是定長碼,  基本上就是unicode編碼的實(shí)現(xiàn). 與CPU字序有關(guān)

          ---------------------------------------糾正后----------------------------

        7. UTF-16是unicode的preferred encoding.
        8. UTF-32, 僅使用了unicode范圍(0到0x10FFFF)的32位編碼, 相當(dāng)于UCS-4的子集.

          UTF與unicode的關(guān)系:

          Unicode是一個(gè)字符集, 可以看作為內(nèi)碼.
          而UTF是一種編碼方式, 它的出現(xiàn)是因?yàn)閡nicode不適宜在某些場合直接傳輸和處理. UTF-16直接就是unicode編碼, 沒有變換, 但它包含了0x00在編碼內(nèi), 頭256字節(jié)碼的第一個(gè)byte都是0x00, 在操作系統(tǒng)(C語言)中有特殊意義, 會(huì)引起問題. 采用UTF-8編碼對unicode的直接編碼作些變換可以避免這問題, 并帶來一些優(yōu)點(diǎn).

          中國國標(biāo)編碼:
        9. GB 13000: 完全等同于ISO 10646-1/Unicode 2.1, 今后也將隨ISO 10646/Unicode的標(biāo)準(zhǔn)更改而同步更改.
        10. GBK: 對GB2312的擴(kuò)充, 以容納GB2312字符集范圍以外的Unicode 2.1的統(tǒng)一漢字部分, 并且增加了部分unicode中沒有的字符.
        11. GB 18030-2000: 基于GB 13000, 作為Unicode 3.0的GBK擴(kuò)展版本, 覆蓋了所有unicode編碼, 地位等同于UTF-8, UTF-16, 是一種unicode編碼形式. 變長編碼, 用單字節(jié)/雙字節(jié)/4字節(jié)對字符編碼. GB18030向下兼容GB2312/GBK.
          GB 18030是中國所有非手持/嵌入式計(jì)算機(jī)系統(tǒng)的強(qiáng)制實(shí)施標(biāo)準(zhǔn).


          -------------------------------

          什么是 UCS 和 ISO 10646?

          國際標(biāo)準(zhǔn) ISO 10646 定義了 通用字符集 (Universal Character Set, UCS). UCS 是所有其他字符集標(biāo)準(zhǔn)的一個(gè)超集. 它保證與其他字符集是雙向兼容的. 就是說, 如果你將任何文本字符串翻譯到 UCS格式, 然后再翻譯回原編碼, 你不會(huì)丟失任何信息.

          UCS 包含了用于表達(dá)所有已知語言的字符. 不僅包括拉丁語,希臘語, 斯拉夫語,希伯來語,阿拉伯語,亞美尼亞語和喬治亞語的描述, 還包括中文, 日文和韓文這樣的象形文字, 以及 平假名, 片假名, 孟加拉語, 旁遮普語果魯穆奇字符(Gurmukhi), 泰米爾語, 印.埃納德語(Kannada), Malayalam, 泰國語, 老撾語, 漢語拼音(Bopomofo), Hangul, Devangari, Gujarati, Oriya, Telugu 以及其他數(shù)也數(shù)不清的語. 對于還沒有加入的語言, 由于正在研究怎樣在計(jì)算機(jī)中最好地編碼它們, 因而最終它們都將被加入. 這些語言包括 Tibetian, 高棉語, Runic(古代北歐文字), 埃塞俄比亞語, 其他象形文字, 以及各種各樣的印-歐語系的語言, 還包括挑選出來的藝術(shù)語言比如 Tengwar, Cirth 和 克林貢語(Klingon). UCS 還包括大量的圖形的, 印刷用的, 數(shù)學(xué)用的和科學(xué)用的符號(hào), 包括所有由 TeX, Postscript, MS-DOS,MS-Windows, Macintosh, OCR 字體, 以及許多其他字處理和出版系統(tǒng)提供的字符.

          ISO 10646 定義了一個(gè) 31 位的字符集. 然而, 在這巨大的編碼空間中, 迄今為止只分配了前 65534 個(gè)碼位 (0x0000 到 0xFFFD). 這個(gè) UCS 的 16位子集稱為 基本多語言面 (Basic Multilingual Plane, BMP). 將被編碼在 16 位 BMP 以外的字符都屬于非常特殊的字符(比如象形文字), 且只有專家在歷史和科學(xué)領(lǐng)域里才會(huì)用到它們. 按當(dāng)前的計(jì)劃, 將來也許再也不會(huì)有字符被分配到從 0x000000 到 0x10FFFF 這個(gè)覆蓋了超過 100 萬個(gè)潛在的未來字符的 21 位的編碼空間以外去了. ISO 10646-1 標(biāo)準(zhǔn)第一次發(fā)表于 1993 年, 定義了字符集與 BMP 中內(nèi)容的架構(gòu). 定義 BMP 以外的字符編碼的第二部分 ISO 10646-2 正在準(zhǔn)備中, 但也許要過好幾年才能完成. 新的字符仍源源不斷地加入到 BMP 中, 但已經(jīng)存在的字符是穩(wěn)定的且不會(huì)再改變了.

          UCS 不僅給每個(gè)字符分配一個(gè)代碼, 而且賦予了一個(gè)正式的名字. 表示一個(gè) UCS 或 Unicode 值的十六進(jìn)制數(shù), 通常在前面加上 "U+", 就象 U+0041 代表字符"拉丁大寫字母A". UCS 字符 U+0000 到 U+007F 與 US-ASCII(ISO 646) 是一致的, U+0000 到 U+00FF 與 ISO 8859-1(Latin-1) 也是一致的. 從 U+E000 到 U+F8FF, 已經(jīng) BMP 以外的大范圍的編碼是為私用保留的.

          什么是組合字符?

          UCS里有些編碼點(diǎn)分配給了 組合字符.它們類似于打字機(jī)上的無間隔重音鍵. 單個(gè)的組合字符不是一個(gè)完整的字符. 它是一個(gè)類似于重音符或其他指示標(biāo)記, 加在前一個(gè)字符后面. 因而, 重音符可以加在任何字符后面. 那些最重要的被加重的字符, 就象普通語言的正字法(orthographies of common languages)里用到的那種, 在 UCS 里都有自己的位置, 以確保同老的字符集的向后兼容性. 既有自己的編碼位置, 又可以表示為一個(gè)普通字符跟隨一個(gè)組合字符的被加重字符, 被稱為 預(yù)作字符(precomposed characters). UCS 里的預(yù)作字符是為了同沒有預(yù)作字符的舊編碼, 比如 ISO 8859, 保持向后兼容性而設(shè)的. 組合字符機(jī)制允許在任何字符后加上重音符或其他指示標(biāo)記, 這在科學(xué)符號(hào)中特別有用, 比如數(shù)學(xué)方程式和國際音標(biāo)字母, 可能會(huì)需要在一個(gè)基本字符后組合上一個(gè)或多個(gè)指示標(biāo)記.

          組合字符跟隨著被修飾的字符. 比如, 德語中的元音變音字符 ("拉丁大寫字母A 加上分音符"), 既可以表示為 UCS 碼 U+00C4 的預(yù)作字符, 也可以表示成一個(gè)普通 "拉丁大寫字母A" 跟著一個(gè)"組合分音符":U+0041 U+0308 這樣的組合. 當(dāng)需要堆疊多個(gè)重音符, 或在一個(gè)基本字符的上面和下面都要加上組合標(biāo)記時(shí), 可以使用多個(gè)組合字符. 比如在泰國文中, 一個(gè)基本字符最多可加上兩個(gè)組合字符.

          什么是 UCS 實(shí)現(xiàn)級(jí)別?

          不是所有的系統(tǒng)都需要支持象組合字符這樣的 UCS 里所有的先進(jìn)機(jī)制. 因此 ISO 10646 指定了下列三種實(shí)現(xiàn)級(jí)別:

          級(jí)別1
          不支持組合字符和 Hangul Jamo 字符 (一種特別的, 更加復(fù)雜的韓國文的編碼, 使用兩個(gè)或三個(gè)子字符來編碼一個(gè)韓文音節(jié))
          級(jí)別2
          類似于級(jí)別1, 但在某些文字中, 允許一列固定的組合字符 (例如, 希伯來文, 阿拉伯文, Devangari, 孟加拉語, 果魯穆奇語, Gujarati, Oriya, 泰米爾語, Telugo, 印.埃納德語, Malayalam, 泰國語和老撾語). 如果沒有這最起碼的幾個(gè)組合字符, UCS 就不能完整地表達(dá)這些語言.
          級(jí)別3
          支持所有的 UCS 字符, 例如數(shù)學(xué)家可以在任意一個(gè)字符上加上一個(gè) tilde(顎化符號(hào),西班牙語字母上面的~)或一個(gè)箭頭(或兩者都加).

          什么是 Unicode?

          歷史上, 有兩個(gè)獨(dú)立的, 創(chuàng)立單一字符集的嘗試. 一個(gè)是國際標(biāo)準(zhǔn)化組織(ISO)的 ISO 10646 項(xiàng)目, 另一個(gè)是由(一開始大多是美國的)多語言軟件制造商組成的協(xié)會(huì)組織的 Unicode 項(xiàng)目. 幸運(yùn)的是, 1991年前后, 兩個(gè)項(xiàng)目的參與者都認(rèn)識(shí)到, 世界不需要兩個(gè)不同的單一字符集. 它們合并雙方的工作成果, 并為創(chuàng)立一個(gè)單一編碼表而協(xié)同工作. 兩個(gè)項(xiàng)目仍都存在并獨(dú)立地公布各自的標(biāo)準(zhǔn), 但 Unicode 協(xié)會(huì)和 ISO/IEC JTC1/SC2 都同意保持 Unicode 和 ISO 10646 標(biāo)準(zhǔn)的碼表兼容, 并緊密地共同調(diào)整任何未來的擴(kuò)展.

          那么 Unicode 和 ISO 10646 不同在什么地方?

          Unicode 協(xié)會(huì)公布的 Unicode 標(biāo)準(zhǔn) 嚴(yán)密地包含了 ISO 10646-1 實(shí)現(xiàn)級(jí)別3的基本多語言面. 在兩個(gè)標(biāo)準(zhǔn)里所有的字符都在相同的位置并且有相同的名字.

          Unicode 標(biāo)準(zhǔn)額外定義了許多與字符有關(guān)的語義符號(hào)學(xué), 一般而言是對于實(shí)現(xiàn)高質(zhì)量的印刷出版系統(tǒng)的更好的參考. Unicode 詳細(xì)說明了繪制某些語言(比如阿拉伯語)表達(dá)形式的算法, 處理雙向文字(比如拉丁與希伯來文混合文字)的算法和 排序與字符串比較 所需的算法, 以及其他許多東西.

          另一方面, ISO 10646 標(biāo)準(zhǔn), 就象廣為人知的 ISO 8859 標(biāo)準(zhǔn)一樣, 只不過是一個(gè)簡單的字符集表. 它指定了一些與標(biāo)準(zhǔn)有關(guān)的術(shù)語, 定義了一些編碼的別名, 并包括了規(guī)范說明, 指定了怎樣使用 UCS 連接其他 ISO 標(biāo)準(zhǔn)的實(shí)現(xiàn), 比如 ISO 6429 和 ISO 2022. 還有一些與 ISO 緊密相關(guān)的, 比如 ISO 14651 是關(guān)于 UCS 字符串排序的.

          考慮到 Unicode 標(biāo)準(zhǔn)有一個(gè)易記的名字, 且在任何好的書店里的 Addison-Wesley 里有, 只花費(fèi) ISO 版本的一小部分, 且包括更多的輔助信息, 因而它成為使用廣泛得多的參考也就不足為奇了. 然而, 一般認(rèn)為, 用于打印 ISO 10646-1 標(biāo)準(zhǔn)的字體在某些方面的質(zhì)量要高于用于打印 Unicode 2.0的. 專業(yè)字體設(shè)計(jì)者總是被建議說要兩個(gè)標(biāo)準(zhǔn)都實(shí)現(xiàn), 但一些提供的樣例字形有顯著的區(qū)別. ISO 10646-1 標(biāo)準(zhǔn)同樣使用四種不同的風(fēng)格變體來顯示表意文字如中文, 日文和韓文 (CJK), 而 Unicode 2.0 的表里只有中文的變體. 這導(dǎo)致了普遍的認(rèn)為 Unicode 對日本用戶來說是不可接收的傳說, 盡管是錯(cuò)誤的.

          什么是 UTF-8?

          首先 UCS 和 Unicode 只是分配整數(shù)給字符的編碼表. 現(xiàn)在存在好幾種將一串字符表示為一串字節(jié)的方法. 最顯而易見的兩種方法是將 Unicode 文本存儲(chǔ)為 2 個(gè) 或 4 個(gè)字節(jié)序列的串. 這兩種方法的正式名稱分別為 UCS-2 和 UCS-4. 除非另外指定, 否則大多數(shù)的字節(jié)都是這樣的(Bigendian convention). 將一個(gè) ASCII 或 Latin-1 的文件轉(zhuǎn)換成 UCS-2 只需簡單地在每個(gè) ASCII 字節(jié)前插入 0x00. 如果要轉(zhuǎn)換成 UCS-4, 則必須在每個(gè) ASCII 字節(jié)前插入三個(gè) 0x00.

          在 Unix 下使用 UCS-2 (或 UCS-4) 會(huì)導(dǎo)致非常嚴(yán)重的問題. 用這些編碼的字符串會(huì)包含一些特殊的字符, 比如 '\0' 或 '/', 它們在 文件名和其他 C 庫函數(shù)參數(shù)里都有特別的含義. 另外, 大多數(shù)使用 ASCII 文件的 UNIX 下的工具, 如果不進(jìn)行重大修改是無法讀取 16 位的字符的. 基于這些原因, 在文件名, 文本文件, 環(huán)境變量等地方, UCS-2 不適合作為 Unicode 的外部編碼.

          在 ISO 10646-1 Annex RRFC 2279 里定義的 UTF-8 編碼沒有這些問題. 它是在 Unix 風(fēng)格的操作系統(tǒng)下使用 Unicode 的明顯的方法.

          UTF-8 有一下特性:

          • UCS 字符 U+0000 到 U+007F (ASCII) 被編碼為字節(jié) 0x00 到 0x7F (ASCII 兼容). 這意味著只包含 7 位 ASCII 字符的文件在 ASCII 和 UTF-8 兩種編碼方式下是一樣的.
          • 所有 >U+007F 的 UCS 字符被編碼為一個(gè)多個(gè)字節(jié)的串, 每個(gè)字節(jié)都有標(biāo)記位集. 因此, ASCII 字節(jié) (0x00-0x7F) 不可能作為任何其他字符的一部分.
          • 表示非 ASCII 字符的多字節(jié)串的第一個(gè)字節(jié)總是在 0xC0 到 0xFD 的范圍里, 并指出這個(gè)字符包含多少個(gè)字節(jié). 多字節(jié)串的其余字節(jié)都在 0x80 到 0xBF 范圍里. 這使得重新同步非常容易, 并使編碼無國界, 且很少受丟失字節(jié)的影響.
          • 可以編入所有可能的 231個(gè) UCS 代碼
          • UTF-8 編碼字符理論上可以最多到 6 個(gè)字節(jié)長, 然而 16 位 BMP 字符最多只用到 3 字節(jié)長.
          • Bigendian UCS-4 字節(jié)串的排列順序是預(yù)定的.
          • 字節(jié) 0xFE 和 0xFF 在 UTF-8 編碼中從未用到.

          下列字節(jié)串用來表示一個(gè)字符. 用到哪個(gè)串取決于該字符在 Unicode 中的序號(hào).

          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

          xxx 的位置由字符編碼數(shù)的二進(jìn)制表示的位填入. 越靠右的 x 具有越少的特殊意義. 只用最短的那個(gè)足夠表達(dá)一個(gè)字符編碼數(shù)的多字節(jié)串. 注意在多字節(jié)串中, 第一個(gè)字節(jié)的開頭"1"的數(shù)目就是整個(gè)串中字節(jié)的數(shù)目.

          例如: Unicode 字符 U+00A9 = 1010 1001 (版權(quán)符號(hào)) 在 UTF-8 里的編碼為:

          11000010 10101001 = 0xC2 0xA9

          而字符 U+2260 = 0010 0010 0110 0000 (不等于) 編碼為:

          11100010 10001001 10100000 = 0xE2 0x89 0xA0

          這種編碼的官方名字拼寫為 UTF-8, 其中 UTF 代表 UCS Transformation Format. 請勿在任何文檔中用其他名字 (比如 utf8 或 UTF_8) 來表示 UTF-8, 當(dāng)然除非你指的是一個(gè)變量名而不是這種編碼本身.

          什么編程語言支持 Unicode?

          在大約 1993 年之后開發(fā)的大多數(shù)現(xiàn)代編程語言都有一個(gè)特別的數(shù)據(jù)類型, 叫做 Unicode/ISO 10646-1 字符. 在 Ada95 中叫 Wide_Character, 在 Java 中叫 char.

          ISO C 也詳細(xì)說明了處理多字節(jié)編碼和寬字符 (wide characters) 的機(jī)制, 1994 年 9 月 Amendment 1 to ISO C 發(fā)表時(shí)又加入了更多. 這些機(jī)制主要是為各類東亞編碼而設(shè)計(jì)的, 它們比處理 UCS 所需的要健壯得多. UTF-8 是 ISO C 標(biāo)準(zhǔn)調(diào)用多字節(jié)字符串的編碼的一個(gè)例子, wchar_t 類型可以用來存放 Unicode 字符.



        12. Author: orangelizq
          email: orangelizq@163.com

          歡迎大家訪問我的個(gè)人網(wǎng)站 萌萌的IT人
          posted on 2007-08-02 16:54 桔子汁 閱讀(616) 評論(0)  編輯  收藏 所屬分類: J2EE
          主站蜘蛛池模板: 开江县| 井陉县| 封开县| 梧州市| 淮南市| 保靖县| 博白县| 江油市| 墨脱县| 通江县| 新田县| 郴州市| 屏南县| 宣化县| 若羌县| 张家界市| 汉寿县| 赤城县| 宁陵县| 新民市| 余庆县| 樟树市| 广元市| 鄂伦春自治旗| 塔城市| 开封市| 双牌县| 阳山县| 西贡区| 佛教| 卫辉市| 益阳市| 富顺县| 瓮安县| 洱源县| 博罗县| 卫辉市| 万全县| 海晏县| 西林县| 汉川市|