字符編碼簡介

亂碼,應該沒有人沒遇到過,多是在打開網頁的時候,多是在打開文本文件的時候,也多是在程序中處理字符串的時候。有的時候問題很容易解決,還有的時候則讓人大傷腦筋。
在寫程序的時候碰到過幾回字符編碼相關的問題後,才發現字符編碼相關的概念應該是程序員必須牢固掌握的基礎知識。字符編碼相關的概念其實不復雜,可是每每上學的時候沒有重視,加上在學校裏面用到的時候也很少,因此大多數人可能都沒弄清基本概念,只知道了幾種碼的名字,好比ascii,gbk等等。
下面簡單介紹字符編碼中的幾個基本概念,具體例子主要是以unicode(也稱爲UCS,即Unicode Character Set)爲例,一些概念的英文名稱也來自Unicode標準。程序員

爲何要須要編碼

這也是很顯然的。在最底層的硬件層面,計算機只能處理二進制的bit 或者 byte,要處理符號,須要將符號和這種二進制數創建一種對應關係,這就是編碼。網絡

字符集(字符庫)

要設計一種編碼,首先要肯定這種編碼是針對哪些字符的,畢竟,這個世界上,人們用於交流的符號種類實在太多了,一種編碼不可能什麼都編進來。這樣一個字符的集合,就是該編碼的字符集(repertoire of abstract characters)。編輯器

碼空間和碼點(codespace & code points)

編碼就是用數字來表示一個符號,每一個符號對應的這個數字(通常都是整數,好像沒據說過不是整數的編碼方式)就是這個符號的碼點(code point), 全部碼點組成的集合就是這種編碼的碼空間(codespace)。
例如unicode的碼空間是從 0 to 10FFFF16, 一共有 1,114,112 碼點。在unicode裏面,表示一個字符和它對應的碼點,是這種格式: 編碼

U+0061 latin small letter a

前綴U+,後面接16進製表示的碼點,後面接對這個字符的描述。spa

編碼形式(Encoding Forms)

若是一種編碼僅僅是給字符集中的每一個字符分配一個code point,那這種編碼是徹底沒法實用的。由於計算機不能抽象的處理數字(這裏說的是硬件層面,不是經過軟件模擬出來的抽象能力)。一個數學上的整數,是一個抽象的概念,要讓計算機處理(包括計算、存儲和經過網絡發送)一個整數,必須明確這個整數在計算機中如何表示,就是要精確到用幾個字節來表示這個整數。這就產生了具體的編碼形式。設計

編碼單元(code unit)

所謂編碼單元,就是一種編碼形式中的最小編碼單位,一個字符的編碼的長度必定最小編碼單元的整數倍。
好比UTF-16的編碼單元是16bit,那麼UTF-16中任何字符的編碼確定是16bit,32bit,48bit..., 而毫不會是8bit或者24bit。固然,實際因爲32bit已經足夠,因此UTF-16不會超過32bit。
計算機中最多見的三種基本數據單元是 8-bit, 16-bit 和 32-bit,而unicode也有三種編碼形式分別以這三種數據單元做爲 code unit,這三種編碼分別叫作utf-8, utf-16, utf-32code

UTF-32

對於utf-32來講,最簡單了,由於32-bit能表示的整數範圍已經徹底容納了unicode的codespace,因此直接將每一個code point用32-bit的二進制整數表示就能夠了。全部字符的編碼都只須要一個code unit。 這種編碼方法的缺點是顯而易見的:太浪費存儲空間和傳輸帶寬了,其實咱們經常使用的字符數量並很少,用兩個字節都夠了,英語國家的人用的字符就就更少了。orm

UTF-16

utf-16就有點麻煩了,一個code unit只有16位,若是想只用一個 code unit表示全部字符是不可能了。解決問題也很簡單:對於那些在 U+0000..U+FFFF之間的碼點,就用一個編碼單元表示,對於 U+10000..U+10FFFF, 就用兩個編碼單元表示。
等等,好像有哪裏不對,仔細一看,就能發現這麼作顯然是有問題的,那些用兩個16-bit表示的code points, 它的每一個16-bit unit又分別表示了一個code point。當計算機從磁盤中讀取一個utf-16編碼的文本文件,它讀入一個16-bit的編碼單元后是應該將這16-bit看成一個字符呢,仍是將這16bit和其後16bit合在一塊兒解釋爲一個字符呢?
因此,其實在 U+0000..U+FFFF之間,有2048個code points (U+D800–U+DFFF) 是不用於表示任何字符的,而是專門保留出來給UTF-16編碼用的,這2048個code points 叫作 Surrogates Area,具體還能夠分爲 high-surrogate code points (U+D800..U+DBFF)以及 Low-Surrogate code points(U+DC00..U+DFFF)。而那些須要用兩個16-bit來表示的code points, 其兩個code unit 都是surrogate區域的數字。utf-8

BMP(Basic Multi-lingual Plane)

對於那些code points在 U+0000..U+FFFF 之間的字符,統稱爲Basic Multi-lingual Plane (BMP),實際上,這部分字符幾乎包含了全部的經常使用字符,因此,用utf-16編碼時,絕大部分時候用到的都是 1個16-bit的編碼,比起utf-32,能省下一半空間。ci

UTF-8

像UTF-16同樣,utf-8也是一種變長的碼(就是不一樣字符的編碼包含的編碼單元的個數不是一致的,有的多,有的少),最短1個字節,最長4個字節,具體的規則此處就不詳述了。
utf-8有幾個好處:

  • 兼容ascii碼,顯然,utf-16和utf-32都作不到這一點
  • 節省空間,對於漢字來講可能不必定,可是對於使用英文的人來講,utf-8使用的空間基本就和ascii編碼同樣了。
  • 沒有字節序問題,這一點在下一節說明。

BOM和字節序

在文本編輯器的格式菜單中,經常能看到這樣一些字眼:BOM,Big Endian, Little Endian。這些都是用於表示編碼的字節序的。肯定了編碼形式後,在存儲和傳輸一個編碼時,發現還有一個問題,就是字節序問題。若是一種編碼的基本編碼單元就是字節,那顯然就啥問題沒有了,由於計算機處理和傳輸的最小單位就是字節。例如ascii碼,UTF-8就沒有這種問題(一個utf8編碼雖然可能有多個字節,但這幾個字節的順序是固定的)。而utf-16和utf-32的基本編碼單元都不是字節,因此當計算機存儲一個基本編碼單元時,屬於該單元的兩個或者4個字節應該以什麼順序存儲就成了問題。(傳輸也有一樣的問題)Unicode規範中推薦的標記字節順序的方法是BOM(Byte Order Mark)。在UCS編碼中有一個叫作"ZERO WIDTH NO-BREAK SPACE"的字符,它的編碼是FEFF。而FFFE在UCS中是不存在的字符,因此不該該出如今實際傳輸中。UCS規範建議咱們在傳輸字節流前(或者文件的最開頭),先傳輸字符"ZERO WIDTH NO-BREAK SPACE"。這樣若是接收者收到FEFF,就代表這個字節流是Big-Endian的;若是收到FFFE,就代表這個字節流是Little-Endian的。所以字符"ZERO WIDTH NO-BREAK SPACE"又被稱做BOM。 UTF-8不須要BOM來代表字節順序,但能夠用BOM來代表編碼方式。字符"ZERO WIDTH NO-BREAK SPACE"的UTF-8編碼是EF BB BF。因此若是接收者收到以EF BB BF開頭的字節流,就知道這是UTF-8編碼了。

相關文章
相關標籤/搜索