字符編碼發(fā)展史4 — Unicode與UTF-8
上一篇《字符編碼發(fā)展史3 — GB2312/Big5/GBK/GB18030》我們講解了ANSI編碼中的GB2312/Big5/GBK/GB18030。本篇我們將繼續(xù)講解字符編碼的第三個(gè)發(fā)展階段中的Unicode與UTF-8。
2.3. 第三個(gè)階段 國(guó)際化
前面提到的第二個(gè)階段,各個(gè)國(guó)家和地區(qū)各自為政,紛紛制定了適用于自己國(guó)家語言的字符編碼(統(tǒng)稱為ANSI碼),確實(shí)能解決該地區(qū)范圍內(nèi)語言文字的信息化處理。
隨著互聯(lián)網(wǎng)的普及和全球網(wǎng)絡(luò)的互聯(lián)互通,計(jì)算機(jī)的信息經(jīng)常需要在全球范圍內(nèi)進(jìn)行分享和傳輸。這時(shí)這些只兼容ASCII碼互相之間卻不兼容的字符編碼就暴露了巨大的缺陷:編碼混亂,這個(gè)混亂常體現(xiàn)在以下幾點(diǎn):
- 文本信息是一個(gè)國(guó)際化的內(nèi)容,包含了多種不同的語言時(shí),根本找不到一個(gè)合適的編碼。如:你的內(nèi)容里既有西歐的法語又中國(guó)的漢字,包含西歐語言的
ISO 8859-1不支持中國(guó)的漢字,包含中國(guó)漢字的GB 18030不支持西歐的字符。 - 編碼和解碼使用的編碼方式不一致時(shí),會(huì)出現(xiàn)亂碼。如以下兩種場(chǎng)景:
- 數(shù)據(jù)在網(wǎng)絡(luò)傳輸時(shí),數(shù)據(jù)發(fā)送用了A編碼(假設(shè)是
ISO 8859-1),數(shù)據(jù)接收時(shí)誤用了B編碼(假設(shè)是GB 18030)去解碼,就會(huì)出現(xiàn)亂碼。 - 網(wǎng)上下載了一個(gè)純文本的txt文檔,里面保存內(nèi)容的編碼方式和本地計(jì)算機(jī)的默認(rèn)編碼不一致也會(huì)出現(xiàn)亂碼。這時(shí)你可能還根本不知道這個(gè)文檔采用的編碼是什么,只能靠猜測(cè),然后通過工具去手動(dòng)轉(zhuǎn)換編碼格式。
為了解決ANSI系列編碼的缺陷,使國(guó)際間信息交流更加方便,國(guó)際標(biāo)準(zhǔn)化組織(ISO)和統(tǒng)一碼聯(lián)盟(Unicode Consortium)共同制定的一個(gè)國(guó)際標(biāo)準(zhǔn)字符集:Unicode。Unicode為各種語言中的每一個(gè)字符設(shè)定了統(tǒng)一并且唯一的數(shù)字編號(hào),以滿足跨語言、跨平臺(tái)進(jìn)行文本轉(zhuǎn)換、處理的要求。
2.3.1. Unicode與UCS
2.3.1.1. 什么是Unicode與UCS?
這里講一個(gè)冷知識(shí),歷史上存在兩個(gè)獨(dú)立的嘗試創(chuàng)立單一字符集的組織,即 國(guó)際標(biāo)準(zhǔn)化組織(ISO)和統(tǒng)一碼聯(lián)盟(Unicode Consortium)。
- 國(guó)際標(biāo)準(zhǔn)化組織 制定了UCS標(biāo)準(zhǔn)(全稱
Universal Character Set),最初稱為ISO/IEC 10646。 - 統(tǒng)一碼聯(lián)盟 制了Unicode標(biāo)準(zhǔn),旨在解決不同字符編碼之間的兼容性問題。
隨著時(shí)間的推移,國(guó)際標(biāo)準(zhǔn)化組織和統(tǒng)一碼聯(lián)盟意識(shí)到各自的標(biāo)準(zhǔn)在目標(biāo)上是一致的,因此決定合作,將UCS和Unicode合并為一個(gè)統(tǒng)一的標(biāo)準(zhǔn)。從Unicode 2.0開始,Unicode標(biāo)準(zhǔn)與ISO/IEC 10646標(biāo)準(zhǔn)保持同步,兩者在字符集和編碼方案上基本一致。
所以,你可以理解為:Unicode和UCS是同一個(gè)東西:國(guó)際標(biāo)準(zhǔn)字符集。現(xiàn)在幾乎統(tǒng)一用Unicode一詞,UCS用的越來越少了。
Unicode是一個(gè)字符集,不是編碼方式,又稱統(tǒng)一碼、萬國(guó)碼、單一碼、標(biāo)準(zhǔn)萬國(guó)碼(其實(shí)都是同一個(gè)東西,不同的叫法)。它收集了世界上幾十種文字系統(tǒng),幾乎包含了世界上用到的所有字符。截止2024年9月,Unicode的最新的版本是16.0.0,發(fā)布于2024年9月10日,總共收錄了154,998個(gè)字符。Unicode 16.0.0標(biāo)準(zhǔn)的官方文檔參見:https://www.unicode.org/versions/Unicode16.0.0/
Unicode的編碼方式有三種:UTF-8、UTF-16、UTF-32。其中UTF-16、UTF-32又分為大端和小端兩種。
2.3.1.2. Unicode字符集的碼點(diǎn)編號(hào)
Unicode字符集給每個(gè)字符根據(jù)其所在的碼點(diǎn)分配了一個(gè)唯一的碼點(diǎn)值,即碼點(diǎn)編號(hào),也叫字符編號(hào),格式為:U+XXXX,其中XXXX為四位十六進(jìn)制數(shù)字。比如,U+0041這個(gè)碼點(diǎn)編號(hào),表示英語大寫字母A。
Unicode的編碼空間將所有字符按照使用的頻率劃分為17個(gè)平面(plane),每個(gè)平面包含2^16(65536)個(gè)碼位,將來根據(jù)需要,還可擴(kuò)展為更多平面。17個(gè)平面的碼位可表示為從U+0000到U+10FFFF,共計(jì)1114112個(gè)碼位。
第0個(gè)平面稱為基本多語言平面(Basic Multilingual Plane),簡(jiǎn)稱基本平面(BMP),或稱第零平面(Plane 0),碼點(diǎn)區(qū)間:U+0000~U+FFFF。它涵蓋了當(dāng)今世界上正在使用的最常用字符,我們平常用到的大多數(shù)常見字符,就是在BMP平面上。BMP平面以外的其他平面叫增補(bǔ)平面(Supplementary Planes),也稱為輔助平面。
Unicode字符集中的U+0000~U+007F(即十進(jìn)制的0~127),跟ASCII表示的字符是一致的;U+0000~U+00FF(即十進(jìn)制的0~255),跟ISO 8859-1字符集(即Latin-1字符集)也是一致的。所以Unicode的碼點(diǎn)編號(hào)是兼容ASCII和ISO 8859-1的。
BMP平面中有一個(gè)私用區(qū)(即PUA:Private Use Area,或?qū)懽鱌UZ:Private Use Zone):0xE000~0xF8FF,共6400個(gè)碼點(diǎn),被保留為私用,Unicode官方未將之分配給任何Unicode字符。還有一個(gè)代理區(qū)(Surrogate Zone):0xD800-0xDFFF,共2048個(gè)碼點(diǎn),代理區(qū)的碼點(diǎn)不定義任何字符,目的是用基本平面BMP中的兩個(gè)碼點(diǎn)“代理”表示BMP以外的其他增補(bǔ)平面中的字符(后文UTF-16中會(huì)詳細(xì)講解)。
Unicode實(shí)際上共定義了三個(gè)私用區(qū),除了上面提到的BMP的0xE000~0xF8FF,還有兩個(gè)分別是:第15平面的U+F0000~U+FFFFD和第16平面的U+100000~U+10FFFD,這兩個(gè)私用區(qū)幾乎包含了整個(gè)第15平面和第16平面。私用區(qū)相當(dāng)于是可以由Unicode官方之外的個(gè)人和機(jī)構(gòu)自由定義字符的特殊區(qū)域,因此私用區(qū)中的同一個(gè)碼點(diǎn),可被分配給不同的字符,具體是哪個(gè)字符,取決于用戶使用的字體文件,從而導(dǎo)致不同的用戶由于安裝了不同的字體文件,有可能所看到的私用字符也不同。
2.3.2. Unicode的編碼方式
對(duì)于被Unicode收錄的字符其編號(hào)(即碼點(diǎn)編號(hào))是唯一且確定的。但是Unicode的編碼實(shí)現(xiàn)方式(出于傳輸、存儲(chǔ)、處理或向后兼容的考慮)卻有不同的幾種:UTF-8、UTF-16、UTF-32。其中UTF的全稱是:Unicode Transformation Format,表示“Unicode碼轉(zhuǎn)換格式”。其中8/16/32分別表示8位(1字節(jié))/16位(2字節(jié))/32位(4字節(jié)),表示一個(gè)字符進(jìn)行編碼所需的最小字節(jié)單元,也稱編碼單元,簡(jiǎn)稱碼元。
2.3.2.1. UTF-8
1. UTF-8的編碼規(guī)則
UTF-8是一種變長(zhǎng)編碼,對(duì)于一個(gè)Unicode的字符被編碼成1至4個(gè)字節(jié)。Unicode編碼與UTF-8的編碼的對(duì)應(yīng)關(guān)系如下表。
| Unicode編碼 | 十進(jìn)制表示 | UTF-8編碼(二進(jìn)制) |
|---|---|---|
U+0000 – U+007F |
0 ~ 127 |
0xxxxxxx |
U+0080 – U+07FF |
128 ~ 2047 |
110xxxxx 10xxxxxx |
U+0800 – U+FFFF |
2048 ~ 65535 |
1110xxxx 10xxxxxx 10xxxxxx |
U+10000 – U+10FFFF |
65536 ~ 2097151 |
11110xxx 10xxxxxx 10xxxxxx 10xxxxxx |
0、110、1110、11110以及10相當(dāng)于UTF-8編碼中各個(gè)字節(jié)的前綴,因此稱之為前綴碼。他們的含義分別如下:
0: 表示單字節(jié)編碼,單字節(jié)時(shí)表明該字符是一個(gè)ASCII字符。110: 表示雙字節(jié)編碼,出現(xiàn)在雙字節(jié)編碼的首字節(jié)。1110: 表示三字節(jié)編碼,出現(xiàn)在三字節(jié)編碼的首字節(jié)。11110: 表示四字節(jié)編碼,出現(xiàn)在四字節(jié)編碼的首字節(jié)。10: 表示該字符是一個(gè)多字節(jié)編碼(2、3、4字節(jié)),10是多字節(jié)編碼中非首字節(jié)的前綴。
UTF-8編碼中的前綴碼起到了很好的區(qū)分和標(biāo)識(shí)的作用,其編碼的解析過程大致如下:
- 當(dāng)解碼程序讀取到一個(gè)字節(jié)的首位為0,表示這是一個(gè)單字節(jié)編碼的ASCII字符;
- 當(dāng)讀取到一個(gè)字節(jié)的首位為1,表示這是一個(gè)非ASCII字符的多字節(jié)編碼字符中的某個(gè)字節(jié)(可能是首字節(jié),也可能是后續(xù)字節(jié)),接下來若繼續(xù)讀取到一個(gè)1,則確定為首字節(jié),再繼續(xù)讀取直到遇見
終結(jié)標(biāo)志0為止,讀取了幾個(gè)1,就表示該字符為幾個(gè)字節(jié)的編碼; - 當(dāng)讀取到一個(gè)字節(jié)的首位為1,緊接著讀取到一個(gè)終結(jié)標(biāo)志0,則該字節(jié)顯然是非ASCII字符的后續(xù)字節(jié)(即非首字節(jié))。
在UTF-8編碼方式中,絕大部分的中文用三個(gè)字節(jié)編碼,部分中文用四個(gè)字節(jié)編碼,舉例如下:
| Unicode | 字符 | UTF-8編碼 |
|---|---|---|
U+0041 |
A | 0x41 |
U+03A9 |
Ω | 0xCE 0xA9 |
U+6653 |
曉 | 0xE6 0x99 0x93 |
U+2A6A5 |
??(四個(gè)龍) | 0xF0 0xAA 0x9A 0xA5 |
2. UTF-8的優(yōu)缺點(diǎn)
- 優(yōu)點(diǎn):
- 向后兼容ASCII編碼;
- 沒有字節(jié)序(大小端)的問題適合網(wǎng)絡(luò)傳輸;
- 存儲(chǔ)英文和拉丁文等字符非常節(jié)省存儲(chǔ)空間。
- 缺點(diǎn):
- 變長(zhǎng)編碼不利于文本處理;
- 對(duì)于CJK等文字比較浪費(fèi)存儲(chǔ)空間。
未完待續(xù)…… 欲知后事如何,且看下回分解。
下回預(yù)告:字符編碼發(fā)展史5 — UTF-16和UTF-32。
歷史文章推薦:
大家好,我是陌塵。
IT從業(yè)10年+, 北漂過也深漂過,目前暫定居于杭州,未來不知還會(huì)飄向何方。
搞了8年C++,也干過2年前端;用Python寫過書,也玩過一點(diǎn)PHP,未來還會(huì)折騰更多東西,不死不休。
感謝大家的關(guān)注,期待與你一起成長(zhǎng)。

浙公網(wǎng)安備 33010602011771號(hào)