Unicode(萬國碼)是一種通用的文字編碼,拉丁字母沿用了ASCII碼只佔一個位元組,大部分字元佔用了2個位元組(包括漢字),隨著Unicode9.0的發表,支持西夏文等特殊字元,65536個位置被佔滿了,於是西夏文使用三個位元組表示。

問題來了,儲存字體的各個位元組之間是沒有分隔符的,Unicode是如何保證不把2個單位元組字元識別成一個雙位元組字元?比如4946即可以表示2個字母「IF」,也可以表示一個漢字「?」


你混淆了 Unicode 碼位(code point)本身編號整數的大小和該碼位存儲為位元組時的位元組數目。注意這是兩個層面的問題,前者只是一個抽象的整數,後者在具體的編碼方案(UTF-8、UTF-16 等)中有明確的規定。


Unicode沒有這種功能。Unicode只是字符集,至於字元在計算機中如何存儲它是不管的。

實現你說的功能的是UTF-8、UTF-16、UTF-32等等編碼。

UTF-16是定長的2個位元組,所以在BMP(Unicode裏每65536也就是2的16次方個字算一個平面plane,第0個平面是基本多語言平面Basic Multiligual Plane,大多數語言都能用這個平面裏的字元表示)裏的字都能表示了。UTF-32也是定長的4個位元組。

上述這兩種編碼其實就是直接用Unicode的碼點來作為編碼,但是這樣就會有大問題,會出現整個位元組都是0的情況,而在C語言中,位元組0是表示字元串結束的。所以計算機界的大牛Ken Thompson就發明瞭UTF-8編碼。

UTF-8是變長的,如果是0(二進位位,下面的0、1都是)開頭,那麼就表示單位元組字元,兼容ASCII編碼。

0xxxxxxx(x表示二進位位,下同)

如果是1開頭的,就表示變長字元,其中第一個位元組開頭的1的數量就表示這個字元有幾個位元組,然後跟一個0。後面的位元組都是10開頭。

110xxxxx 10xxxxxx

1110xxxx 10xxxxxx 10xxxxxx

11110xxx 10xxxxxx 10xxxxxx 10xxxxxx

x的部分就是Unicode的碼點的二進位表示(去除先導0之後從後往前填入,不足補0)。這樣1到4位元組編碼能容納的二進位位數就分別是7、11、16、21。

比如「中」字,就是U+4E2D,也就是二進位01001110 00101101,去掉先導0以後有15位,對比發現只有三位元組編碼才夠裝下,所以UTF-8編碼就是11100100 10111000 10101101,也就是十六進位E4 B8 AD。

這種現象其實並不罕見。

比如ASCII,其實才定義了7位編碼,一般我們使用時會在最高位補足一位0湊夠一個位元組。

很多其他其實都是在這個最高位上做文章,我們常見的編碼(包括今天介紹的UTF-8,以及曾經很常用的GB2312,GBK,歐洲用的Latin-1等等)都是最高位0表示ASCII兼容的字元,而最高位1的表示擴展字元,像GB2312這樣的雙位元組字元,其實可以擴展出2的14次方也就是16384個字(因為要排除兩個位元組的最高位1,所以兩個位元組纔有14位可用),當然由GB2312編碼的轉換方式的原因,實際上沒有那麼多。

Unicode相當於GB2312的區位碼,區位碼不是直接用來存儲的,需要經過轉換變成國標碼,纔是用來在計算機中存儲的內碼。


推薦閱讀:
相關文章