字元編碼發展史4 — Unicode與UTF-8

陌尘(MoChen)發表於2024-09-27

上一篇《字元編碼發展史3 — GB2312/Big5/GBK/GB18030》我們講解了ANSI編碼中的GB2312/Big5/GBK/GB18030。本篇我們將繼續講解字元編碼的第三個發展階段中的Unicode與UTF-8。

2.3. 第三個階段 國際化

前面提到的第二個階段,各個國家和地區各自為政,紛紛制定了適用於自己國家語言的字元編碼(統稱為ANSI碼),確實能解決該地區範圍內語言文字的資訊化處理。

隨著網際網路的普及和全球網路的互聯互通,計算機的資訊經常需要在全球範圍內進行分享和傳輸。這時這些只相容ASCII碼互相之間卻不相容的字元編碼就暴露了巨大的缺陷:編碼混亂,這個混亂常體現在以下幾點:

  1. 文字資訊是一個國際化的內容,包含了多種不同的語言時,根本找不到一個合適的編碼。如:你的內容裡既有西歐的法語又中國的漢字,包含西歐語言的ISO 8859-1不支援中國的漢字,包含中國漢字的GB 18030不支援西歐的字元。
  2. 編碼和解碼使用的編碼方式不一致時,會出現亂碼。如以下兩種場景:
  • 資料在網路傳輸時,資料傳送用了A編碼(假設是ISO 8859-1),資料接收時誤用了B編碼(假設是GB 18030)去解碼,就會出現亂碼。
  • 網上下載了一個純文字的txt文件,裡面儲存內容的編碼方式和本地計算機的預設編碼不一致也會出現亂碼。這時你可能還根本不知道這個文件採用的編碼是什麼,只能靠猜測,然後透過工具去手動轉換編碼格式。

為了解決ANSI系列編碼的缺陷,使國際間資訊交流更加方便,國際標準化組織(ISO)和統一碼聯盟(Unicode Consortium)共同制定的一個國際標準字符集:Unicode。Unicode為各種語言中的每一個字元設定了統一併且唯一的數字編號,以滿足跨語言、跨平臺進行文字轉換、處理的要求。

2.3.1. Unicode與UCS

2.3.1.1. 什麼是Unicode與UCS?

這裡講一個冷知識,歷史上存在兩個獨立的嘗試創立單一字符集的組織,即 國際標準化組織(ISO)和統一碼聯盟(Unicode Consortium)。

  • 國際標準化組織 制定了UCS標準(全稱Universal Character Set),最初稱為ISO/IEC 10646。
  • 統一碼聯盟 制了Unicode標準,旨在解決不同字元編碼之間的相容性問題。

隨著時間的推移,國際標準化組織和統一碼聯盟意識到各自的標準在目標上是一致的,因此決定合作,將UCS和Unicode合併為一個統一的標準。從Unicode 2.0開始,Unicode標準與ISO/IEC 10646標準保持同步,兩者在字符集和編碼方案上基本一致。

所以,你可以理解為:Unicode和UCS是同一個東西:國際標準字符集。現在幾乎統一用Unicode一詞,UCS用的越來越少了。

Unicode是一個字符集,不是編碼方式,又稱統一碼萬國碼單一碼標準萬國碼(其實都是同一個東西,不同的叫法)。它收集了世界上幾十種文字系統,幾乎包含了世界上用到的所有字元。截止2024年9月,Unicode的最新的版本是16.0.0,釋出於2024年9月10日,總共收錄了154,998個字元。Unicode 16.0.0標準的官方文件參見:https://www.unicode.org/versions/Unicode16.0.0/

Unicode的編碼方式有三種:UTF-8、UTF-16、UTF-32。其中UTF-16、UTF-32又分為大端和小端兩種。

2.3.1.2. Unicode字符集的碼點編號

Unicode字符集給每個字元根據其所在的碼點分配了一個唯一的碼點值,即碼點編號,也叫字元編號,格式為:U+XXXX,其中XXXX為四位十六進位制數字。比如,U+0041這個碼點編號,表示英語大寫字母A

Unicode的編碼空間將所有字元按照使用的頻率劃分為17個平面(plane),每個平面包含2^16(65536)個碼位,將來根據需要,還可擴充套件為更多平面。17個平面的碼位可表示為從U+0000U+10FFFF,共計1114112個碼位。

第0個平面稱為基本多語言平面(Basic Multilingual Plane),簡稱基本平面(BMP),或稱第零平面(Plane 0),碼點區間:U+0000~U+FFFF。它涵蓋了當今世界上正在使用的最常用字元,我們平常用到的大多數常見字元,就是在BMP平面上。BMP平面以外的其他平面叫增補平面(Supplementary Planes),也稱為輔助平面

Unicode字符集中的U+0000~U+007F(即十進位制的0~127),跟ASCII表示的字元是一致的;U+0000~U+00FF(即十進位制的0~255),跟ISO 8859-1字符集(即Latin-1字符集)也是一致的。所以Unicode的碼點編號是相容ASCII和ISO 8859-1的。

BMP平面中有一個私用區(即PUA:Private Use Area,或寫作PUZ:Private Use Zone):0xE000~0xF8FF,共6400個碼點,被保留為私用,Unicode官方未將之分配給任何Unicode字元。還有一個代理區(Surrogate Zone):0xD800-0xDFFF,共2048個碼點,代理區的碼點不定義任何字元,目的是用基本平面BMP中的兩個碼點“代理”表示BMP以外的其他增補平面中的字元(後文UTF-16中會詳細講解)。

Unicode實際上共定義了三個私用區,除了上面提到的BMP的0xE000~0xF8FF,還有兩個分別是:第15平面的U+F0000~U+FFFFD和第16平面的U+100000~U+10FFFD,這兩個私用區幾乎包含了整個第15平面和第16平面。私用區相當於是可以由Unicode官方之外的個人和機構自由定義字元的特殊區域,因此私用區中的同一個碼點,可被分配給不同的字元,具體是哪個字元,取決於使用者使用的字型檔案,從而導致不同的使用者由於安裝了不同的字型檔案,有可能所看到的私用字元也不同。

2.3.2. Unicode的編碼方式

對於被Unicode收錄的字元其編號(即碼點編號)是唯一且確定的。但是Unicode的編碼實現方式(出於傳輸、儲存、處理或向後相容的考慮)卻有不同的幾種:UTF-8、UTF-16、UTF-32。其中UTF的全稱是:Unicode Transformation Format,表示“Unicode碼轉換格式”。其中8/16/32分別表示8位(1位元組)/16位(2位元組)/32位(4位元組),表示一個字元進行編碼所需的最小位元組單元,也稱編碼單元,簡稱碼元

2.3.2.1. UTF-8

1. UTF-8的編碼規則

UTF-8是一種變長編碼,對於一個Unicode的字元被編碼成1至4個位元組。Unicode編碼與UTF-8的編碼的對應關係如下表。

Unicode編碼 十進位制表示 UTF-8編碼(二進位制)
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相當於UTF-8編碼中各個位元組的字首,因此稱之為字首碼。他們的含義分別如下:

  • 0: 表示單位元組編碼,單位元組時表明該字元是一個ASCII字元。
  • 110: 表示雙位元組編碼,出現在雙位元組編碼的首位元組。
  • 1110: 表示三位元組編碼,出現在三位元組編碼的首位元組。
  • 11110: 表示四位元組編碼,出現在四位元組編碼的首位元組。
  • 10: 表示該字元是一個多位元組編碼(2、3、4位元組),10是多位元組編碼中非首位元組的字首。

UTF-8編碼中的字首碼起到了很好的區分和標識的作用,其編碼的解析過程大致如下:

  1. 當解碼程式讀取到一個位元組的首位為0,表示這是一個單位元組編碼的ASCII字元;
  2. 當讀取到一個位元組的首位為1,表示這是一個非ASCII字元的多位元組編碼字元中的某個位元組(可能是首位元組,也可能是後續位元組),接下來若繼續讀取到一個1,則確定為首位元組,再繼續讀取直到遇見終結標誌0為止,讀取了幾個1,就表示該字元為幾個位元組的編碼;
  3. 當讀取到一個位元組的首位為1,緊接著讀取到一個終結標誌0,則該位元組顯然是非ASCII字元的後續位元組(即非首位元組)。

在UTF-8編碼方式中,絕大部分的中文用三個位元組編碼,部分中文用四個位元組編碼,舉例如下:

Unicode 字元 UTF-8編碼
U+0041 A 0x41
U+03A9 Ω 0xCE 0xA9
U+6653 0xE6 0x99 0x93
U+2A6A5 𪚥(四個龍) 0xF0 0xAA 0x9A 0xA5
2. UTF-8的優缺點
  • 優點:
    • 向後相容ASCII編碼;
    • 沒有位元組序(大小端)的問題適合網路傳輸;
    • 儲存英文和拉丁文等字元非常節省儲存空間。
  • 缺點:
    • 變長編碼不利於文字處理;
    • 對於CJK等文字比較浪費儲存空間。

未完待續…… 欲知後事如何,且看下回分解。

下回預告:字元編碼發展史5 — UTF-16和UTF-32。

歷史文章推薦:

字元編碼發展史3 — GB2312/Big5/GBK/GB18030

字元編碼發展史2 — ISO-8859-N

字元編碼發展史1 — ASCII和EASCII


大家好,我是陌塵。

IT從業10年+, 北漂過也深漂過,目前暫定居於杭州,未來不知還會飄向何方。

搞了8年C++,也幹過2年前端;用Python寫過書,也玩過一點PHP,未來還會折騰更多東西,不死不休。

感謝大家的關注,期待與你一起成長。



【SunLogging】
字元編碼發展史4 — Unicode與UTF-8
掃碼二維碼,關注微信公眾號,閱讀更多精彩內容

相關文章