C語言:Unicode字符集


Unicode 也稱為統一碼、萬國碼;看名字就知道,Unicode 希望統一所有國家的字符編碼。

Unicode 於 1994 年正式公布第一個版本,現在的規模可以容納 100 多萬個符號,是一個很大的集合。

有興趣的讀取可以轉到  https://unicode-table.com/cn/ 查看 Unicode 包含的所有字符,以及各個國家的字符是如何分布的。
Windows、Linux、Mac OS 等常見操作系統都已經從底層(內核層面)開始支持 Unicode,大部分的網頁和軟件也使用 Unicode,Unicode 是大勢所趨。

不過由於歷史原因,目前的計算機仍然安裝了 ASCII 編碼以及 GB2312、GBK、Big5、Shift-JIS 等地區編碼,以支持不使用 Unicode 的軟件或者文檔。內核在處理字符時,一般會將地區編碼先轉換為 Unicode,再進行下一步處理。

Unicode 字符集是如何存儲的

本節我們多次說 Unicode 是一套字符集,而不是一套字符編碼,它們之間究竟有什么區別呢?

嚴格來說,字符集和字符編碼不是一個概念:

  • 字符集定義了字符和二進制的對應關系,為每個字符分配了唯一的編號。可以將字符集理解成一個很大的表格,它列出了所有字符和二進制的對應關系,計算機顯示文字或者存儲文字,就是一個查表的過程。
  • 而字符編碼規定了如何將字符的編號存儲到計算機中。如果使用了類似 GB2312 和 GBK 的變長存儲方案(不同的字符占用的字節數不一樣),那么為了區分一個字符到底使用了幾個字節,就不能將字符的編號直接存儲到計算機中,字符編號在存儲之前必須要經過轉換,在讀取時還要再逆向轉換一次,這套轉換方案就叫做字符編碼。


有的字符集在制定時就考慮到了編碼的問題,是和編碼結合在一起的,例如 ASCII、GB2312、GBK、BIG5 等,所以無論稱作字符集還是字符編碼都無所謂,也不好區分兩者的概念。而有的字符集只管制定字符的編號,至於怎么存儲,那是字符編碼的事情,Unicode 就是一個典型的例子,它只是定義了全球文字的唯一編號,我們還需要 UTF-8、UTF-16、UTF-32 這幾種編碼方案將 Unicode 存儲到計算機中。

Unicode 可以使用的編碼方案有三種,分別是:

  • UTF-8:一種變長的編碼方案,使用 1~6 個字節來存儲;
  • UTF-32:一種固定長度的編碼方案,不管字符編號大小,始終使用 4 個字節來存儲;
  • UTF-16:介於 UTF-8 和 UTF-32 之間,使用 2 個或者 4 個字節來存儲,長度既固定又可變。


UTF 是 Unicode Transformation Format 的縮寫,意思是“Unicode轉換格式”,后面的數字表明至少使用多少個比特位(Bit)來存儲字符。

1) UTF-8

UTF-8 的編碼規則很簡單:

  • 如果只有一個字節,那么最高的比特位為 0,這樣可以兼容 ASCII;
  • 如果有多個字節,那么第一個字節從最高位開始,連續有幾個比特位的值為 1,就使用幾個字節編碼,剩下的字節均以 10 開頭。


具體的表現形式為:

  • 0xxxxxxx:單字節編碼形式,這和 ASCII 編碼完全一樣,因此 UTF-8 是兼容 ASCII 的;
  • 110xxxxx 10xxxxxx:雙字節編碼形式(第一個字節有兩個連續的 1);
  • 1110xxxx 10xxxxxx 10xxxxxx:三字節編碼形式(第一個字節有三個連續的 1);
  • 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx:四字節編碼形式(第一個字節有四個連續的 1)。


xxx 就用來存儲 Unicode 中的字符編號。

下面是一些字符的 UTF-8 編碼實例(綠色部分表示本來的 Unicode 編號):

字符 字母N 符號æ 中文
Unicode 編號(二進制) 01001110 11100110 00101110 11101100
Unicode 編號(十六進制) 4E E6 2E EC
UTF-8 編碼(二進制) 01001110 11000011 10100110 11100010 10111011 10101100
UTF-8 編碼(十六進制) 4E C3 A6 E2 BB AC


對於常用的字符,它的 Unicode 編號范圍是 0 ~ FFFF,用 1~3 個字節足以存儲,只有及其罕見,或者只有少數地區使用的字符才需要 4~6個字節存儲。

2) UTF-32

UTF-32 是固定長度的編碼,始終占用 4 個字節,足以容納所有的 Unicode 字符,所以直接存儲 Unicode 編號即可,不需要任何編碼轉換。浪費了空間,提高了效率。

3) UTF-16

UFT-16 比較奇葩,它使用 2 個或者 4 個字節來存儲。

對於 Unicode 編號范圍在 0 ~ FFFF 之間的字符,UTF-16 使用兩個字節存儲,並且直接存儲 Unicode 編號,不用進行編碼轉換,這跟 UTF-32 非常類似。

對於 Unicode 編號范圍在 10000~10FFFF 之間的字符,UTF-16 使用四個字節存儲,具體來說就是:將字符編號的所有比特位分成兩部分,較高的一些比特位用一個值介於 D800~DBFF 之間的雙字節存儲,較低的一些比特位(剩下的比特位)用一個值介於 DC00~DFFF 之間的雙字節存儲。

如果你不理解什么意思,請看下面的表格:

Unicode 編號范圍
(十六進制)
具體的 Unicode 編號
(二進制)
UTF-16 編碼 編碼后的
字節數
0000 0000 ~ 0000 FFFF xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx 2
0001 0000---0010 FFFF yyyy yyyy yyxx xxxx xxxx 110110yy yyyyyyyy 110111xx xxxxxxxx 4


位於 D800~0xDFFF 之間的 Unicode 編碼是特別為四字節的 UTF-16 編碼預留的,所以不應該在這個范圍內指定任何字符。如果你真的去查看 Unicode 字符集,會發現這個區間內確實沒有收錄任何字符。

UTF-16 要求在制定 Unicode 字符集時必須考慮到編碼問題,所以真正的 Unicode 字符集也不是隨意編排字符的。

對比以上三種編碼方案

首先,只有 UTF-8 兼容 ASCII,UTF-32 和 UTF-16 都不兼容 ASCII,因為它們沒有單字節編碼。

1) UTF-8 使用盡量少的字節來存儲一個字符,不但能夠節省存儲空間,而且在網絡傳輸時也能節省流量,所以很多純文本類型的文件(例如各種編程語言的源文件、各種日志文件和配置文件等)以及絕大多數的網頁(例如百度、新浪、163等)都采用 UTF-8 編碼。

UTF-8 的缺點是效率低,不但在存儲和讀取時都要經過轉換,而且在處理字符串時也非常麻煩。例如,要在一個 UTF-8 編碼的字符串中找到第 10 個字符,就得從頭開始一個一個地檢索字符,這是一個很耗時的過程,因為 UTF-8 編碼的字符串中每個字符占用的字節數不一樣,如果不從頭遍歷每個字符,就不知道第 10 個字符位於第幾個字節處,就無法定位。

不過,隨着算法的逐年精進,UTF-8 字符串的定位效率也越來越高了,往往不再是槽點了。

2) UTF-32 是“以空間換效率”,正好彌補了 UTF-8 的缺點,UTF-32 的優勢就是效率高:UTF-32 在存儲和讀取字符時不需要任何轉換,在處理字符串時也能最快速地定位字符。例如,在一個 UTF-32 編碼的字符串中查找第 10 個字符,很容易計算出它位於第 37 個字節處,直接獲取就行,不用再逐個遍歷字符了,沒有比這更快的定位字符的方法了。

但是,UTF-32 的缺點也很明顯,就是太占用存儲空間了,在網絡傳輸時也會消耗很多流量。我們平常使用的字符編碼值一般都比較小,用一兩個字節存儲足以,用四個字節簡直是暴殄天物,甚至說是不能容忍的,所以 UTF-32 在應用上不如 UTF-8 和 UTF-16 廣泛。

3) UTF-16 可以看做是 UTF-8 和 UTF-32 的折中方案,它平衡了存儲空間和處理效率的矛盾。對於常用的字符,用兩個字節存儲足以,這個時候 UTF-16 是不需要轉換的,直接存儲字符的編碼值即可。

Windows 內核、.NET Framework、Cocoa、Java String 內部采用的都是 UTF-16 編碼。UTF-16 是幕后的功臣,我們在編輯源代碼和文檔時都是站在前台,所以一般感受不到,其實很多文本在后台處理時都已經轉換成了 UTF-16 編碼。

不過,UNIX 家族的操作系統(Linux、Mac OS、iOS 等)內核都采用 UTF-8 編碼,我們就不去爭論誰好誰壞了。

寬字符和窄字符(多字節字符)

有的編碼方式采用 1~n 個字節存儲,是變長的,例如 UTF-8、GB2312、GBK 等;如果一個字符使用了這種編碼方式,我們就將它稱為多字節字符,或者窄字符。

有的編碼方式是固定長度的,不管字符編號大小,始終采用 n 個字節存儲,例如 UTF-32、UTF-16 等;如果一個字符使用了這種編碼方式,我們就將它稱為寬字符。

Unicode 字符集可以使用窄字符的方式存儲,也可以使用寬字符的方式存儲;GB2312、GBK、Shift-JIS 等國家編碼一般都使用窄字符的方式存儲;ASCII 只有一個字節,無所謂窄字符和寬字符。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM