Unicode 字符和UTF編碼的理解

  Unicode 編碼的由來瀏覽器

  咱們都知道,計算機的內部所有是由二進制數字0, 1 組成的, 那麼計算機就沒有辦法保存咱們的文字, 這怎麼行呢? 因而美國人就想了一個辦法(計算機是由美國人發明的),也把文字轉化成數字,計算機不就可以保存文字了,因此美國人就制定了一張表,規定了文字與數字的一一對應,字符A  就對應數字65, 字符B 就對應數字66, 這張表就是著名的ASCII 碼錶。因爲美國人的文字比較少,就是a, b, c d 等等, 對應完了,發現一共使用了128個數字,這也太少了,一個字節都沒有使用了,因此就決定用一個字節來表示一個字符, 因此對於ASCII 碼來講,一個字符在計算機中就佔用一個字節。碼錶制定好了,生產計算機的時候直接把碼錶內置到計算機中就能夠了。ui

  可是隨着計算機的推廣,它到達了歐洲,亞洲,這就有點問題了,由於計算機中只有英文,它不可能表達 和書寫其餘國家的語言,好比漢語,日語等等, 這確定也是不行的。因而各個國家的政府就制定各自的碼錶, 好讓計算機也能表示本國的語言,就拿我國來講吧,GBK, GB2313 碼錶就出現了。 這就會出現一個問題,相同的數字在不一樣的碼錶中對應的文字可能不一樣, 這就有可能形成亂碼。國際友人發了一封電子郵件過來,打開一看亂碼了,各國之間的文件不可以交換使用。這時國際標準化組織就想把各國的字符都統一塊兒來,把它們放到一張碼錶中,若是計算機中都內置這張表,那就不會出現亂碼了。和ASCII碼的想法一致,在這張表中,也是給每個字符都分配一個獨一無二的數字。這張表就是Unicode碼錶或Unicode編碼字符集,這每個字符對應的數字稱之爲作碼點(Code Point). 碼錶的樣子以下編碼

 

  Unicode 字符集在計算機中的實現spa

  Unicode 字符集的基礎很是簡單,就是給世界上的每個文字都分配一個獨一無二的數字,這個數字稱之爲碼點(code point), 好比 給字符A分配的數字是65, 給字符B 分配的數字是66,那麼 A的 碼點(code point)就是65, B 的code point 就是66.  但Unicode 相對於其餘編碼又是很是複雜的,這主要是在於Unicode 字符在計算機中的實現上,這些數字怎麼在計算機中表示,用多少個字節?代理

  因爲當時提出制定Unicode字符集標準的時候,是在1990s 左右,那時各國的文字都比較少,都對應完了以後,發現並無超過6萬,正好在計算機中,兩個字節就能夠表示6萬多個字,兩個字節就是16個位(bit),無符號的話那麼最大的數就是16個位都是1,這個數就是 2的16次方即 2^16-1 = 65536-1 = 65535因此就決定用2個字節,16個bit 來表示Unicode 字符, 這就是最先的UCS-2編碼, 這16個bit 稱之爲code uint(碼元), 這時 一個碼點就對應一個碼元。code

  可是隨着時間的推移,尤爲是亞洲國家把大量的象形文字也加入到Unicode 編碼中,忽然發現,65536的數量根本就裝不下這些文字,因而Unicode 編碼字符集也相應的進行了擴充,它把整個字符集分紅了17個平面,每個平面都能放2^16 個文字, 第一個平面就放置最初的UCS-2編碼中所定義的文字,叫作基本多文本平面Basic Multilingual Plane (or BMP), 因此基本多文本平面碼點的取值範圍是0x0000 - 0xFFFF, 其餘的16個平面稱爲補充平面(Supplementary Planes or Astral Planes), 它們的取值範圍從0x10000 開始 (0xFFFF + 1 就等於0x10000,  16進制的計算)  。 針對這種多平面的字符集,出現了不一樣的在計算機中的實現方式,那就是咱們常常聽到的UTF-8, UTF-16, UTF-32. blog

  UTF-16 就是對於在BMP 平面的文字用兩個字節(16個bit)進行表示,而後對於其餘平面的文字用四個字節進行表示,四個字節表示稱之爲surrogate pairs(代理對), 一對16 bit 來表示一個 字符。 因爲一個16bit 稱之爲碼元code unit, 一個字符對應的數字稱爲碼點,這也就意味着,一個碼點要有2個碼元進行表示。內存

  那怎麼才能讓一個碼點用兩個碼元來顯示呢? 先看一下目前Unicode 非基本面的有多少個字, 一共16個面, 一個面是2^16 個字,那也就是說,一共有16 * 2^16 個字, 就是2^20個字符, 也就須要20個二進制bit 位,一個碼元只能16bit 位,那隻能把20bit 位進行拆分紅2個部分,一個部分佔10個bit, 這樣一個碼元就能夠表示了。但這又存在另一個問題,當計算機去讀取字符的時候,它碰到一個字節就去讀取一個字節,它並不知道,你這個字節是拆分出來的,你會發現,原本一個字,確顯示出了絕不相干的兩個字,這怎麼辦呢?其實在usc-2 的時代,它所定義的Unicode 字符集並無徹底使用了2個字節,2個字節的後面一部分是空的,並無對應任何字符,這一段是0xD800 到 0xDFFF(55296 - 65535), 這也就意味着,使用這一段上的碼元沒有任何問題。那咱們就想辦法,讓非基本面的碼點對應到這一段上來。對應的邏輯是這樣的,首先把0xD800 - 0xDFFF  分爲兩個部分,0xD800 到 0xDBFF 和0xDC00 到 0xDFFF,而後再用碼點減去65536, 把剩下的數用二進制表示,若是二進制不夠20位,能夠在前面補0.  再把20位二進制分爲兩個部分,每一個部分佔用10個bit,  前10個bit 對應到0xD800 - 0xDFFF, 稱爲高位(H), 後10個bit 對應到0xDC00 到 0xDFFF 稱爲低位(L), 這樣一個碼點就能夠用兩個碼元(高位, 低位)進行表示了。映射比較複雜,不過Unicode 提供了一個公式,能夠直接經過碼點獲得高低位碼元。it

H = Math.floor((c-0x10000) / 0x400)+0xD800  // c 是碼點 0x10000 就是65536  0x400 就是1024, 0xD800 就是H 開始位置

L = (c - 0x10000) % 0x400 + 0xDC00  // 0xDC00  L 位開始的位置。 

  舉一個小栗子,漢字"𠮷"的碼點爲134071, 大於65535, 它要用4個字節(2個碼元)進行表示。把碼點134071代入到上面的c 中, 因爲瀏覽器的控制檯能夠直接計算16進制,咱們把134071 轉化成16進制 0x20BB7class

  獲得十進制55362, 轉化成16進製爲0xD842, 此爲高位

  十進制57271,轉化成16進製爲0xDFB7, 此爲低位。

  漢字"𠮷"的 UTF-16 編碼爲0xD842 0xDFB7。

  當使用這種方式用兩個碼元去表示一個碼點時,計算機也不會出錯,由於當它讀取到第一個碼元的時候,發現並無對應的字符,它會就繼續向下取第二個碼元,兩個碼元連起來,就是對應的碼點,就能夠顯示一個字了。記住,計算機中只有碼元,碼點是Unicode 字符集中的概念。

  UTF-8也是採用變長字節,不過,它變化幅度有點大,1 , 2, 3, 4 個字節都能用上,它定義了一套規則

    UTF-32 則是用32個bit, 4個字節來表示一個字符,全部的字符都佔用4個字節, 這確定沒有問題,但也形成了內存空間的浪費, 因此使用不是很普遍。

相關文章
相關標籤/搜索