天天看點

字元編碼淺談

你是否認為“ascii碼 = 一個字元就是8比特”?你是否認為一個位元組就是一個字元,一個字元就是8比特?你是否還認為你是否還認為utf-8就是用8比特表示一個字元?如果真的是這樣認為認真讀完這篇文章吧!

<a target="_blank"></a>

首先大家需要明确的是在計算機裡所有的資料都是位元組的形式存儲,處理的。我們需要這些位元組來表示計算機裡的資訊。但是這些位元組本身又是沒有任何意義的,是以我們需要對這些位元組賦予實際的意義。是以才會制定各種編碼标準。

首先需要明确的是存在兩種編碼模型

在這種編碼模型裡,一個字元集定義了這個字元集裡包含什麼字元,同時把每個字元如何對應成計算機裡的比特也進行了定義。例如ascii,在ascii裡直接定義了a -&gt; 0100 0001。

在現代編碼模型裡要知道一個字元如何映射成計算機裡比特,需要經過如下幾個步驟。

知道一個系統需要支援哪些字元,這些字元的集合被稱為字元表(character repertoire)

給字元表裡的抽象字元編上一個數字,也就是字元集合到一個整數集合的映射。這種映射稱為編碼字元集(ccs:coded character set),unicode是屬于這一層的概念,跟計算機裡的什麼進制啊沒有任何關系,它是完全數學的抽象的。

将ccs裡字元對應的整數轉換成有限長度的比特值,便于以後計算機使用一定長度的二進制形式表示該整數。這個對應關系被稱為字元編碼表(cef:character encoding form)utf-8, utf-16都屬于這層。

對于cef得到的比特值具體如何在計算機中進行存儲,傳輸。因為存在大端小端的問題,這就會跟具體的作業系統相關了。這種解決方案稱為字元編碼方案(ces:character encoding scheme)。

平常我們所說的編碼都在第三步的時候完成了,都沒有涉及到ces。是以ces并不在本文的讨論範圍之内。

現在也許有人會想為什麼要有現代的編碼模型?為什麼在現在的編碼模型要拆分出這麼多概念?直接像原始的編碼模型直接都規定好所有的資訊不行嗎?這些問題在下文的編碼發展史中都會有所闡述。

ascii出現在上個世紀60年代的美國,ascii一共定義了128個字元,使用了一個位元組的7位。定義的這些字元包括英文字母a-z,a-z,數字0-9,一些标點符号和控制符号。在shell裡輸入man ascii,可以看到完整的ascii字元集。ascii采用的編碼模型是簡單字元集,它直接定義了一個字元的比特值表示。裡例如上文提到的a -&gt; 0100 0001。也就是ascii直接完成了現代編碼模型的前三步工作。

在英語系國家裡ascii标準很完美。但是不要忘了世界上可有好幾千種語言,這些語言裡不僅隻有這些符号啊。如果使用這些語言的人也想使用計算機,ascii就遠遠不夠了。到這裡編碼進入了混亂的時代。

人們知道計算機的一個位元組是8位,可以表示256個字元。ascii卻隻使用了7位,是以人們決定把剩餘的一位也利用起來。這時問題出現了,人們對 于已經規定好的128個字元是沒有異議的,但是不同語系的人對于其他字元的需求是不一樣的,是以對于剩下的128個字元的擴充會千奇百怪。而且更加混亂的 是,在亞洲的語言系統中有更多的字元,一個位元組無論如何也滿足不了需求了。例如僅漢字就有10萬多個,一個位元組的256表示方式怎麼能夠滿足呢。于是就又 産生了各種多位元組的表示一個字元方法(gbk就是其中一種),這就使整個局面更加的混亂不堪。(希望看到這裡的你不再認為一個位元組就是一個字元,一個字元 就是8比特)。每個語系都有自己特定的編碼頁(code pages)的狀況,使得不同的語言出現在同一台計算機上,不同語系的人在網絡上進行交流都成了癡人說夢。這時unicode出現了。

unicode就是給計算機中所有的字元各自配置設定一個代号。unicode通俗來說是什麼呢?就是現在實作共産主義了,各國人民不在需要自己特定的 國家身份證,而是給每人一張全世界通用的身份證。unicode是屬于編碼字元集(ccs)的範圍。unicode所做的事情就是将我們需要表示的字元表 中的每個字元映射成一個數字,這個數字被稱為相應字元的碼點(code point)。例如“嚴”字在unicode中對應的碼點是u+0x4e25。

到目前為止,我們隻是找到了一堆字元和數字之間的映射關系而已,隻到了ccs的層次。這些數字如何在計算機和網絡中存儲和展示還沒有提到。

前面還都屬于字元集的概念,現在終于到cef的層次了。為了便于計算的存儲和處理,現在我們要把哪些純數學數字對應成有限長度的比特值了。最直覺的 設計當然是一個字元的碼點是什麼數字,我們就把這個數字轉換成相應的二進制表示,例如“嚴”在unicode中對應的數字是0x4e25,他的二進制是100 1110 0010 0101, 也就是嚴這個字需要兩個位元組進行存儲。按照這種方法大部分漢字都可以用兩個位元組來表示了。但是還有其他語系的存在,沒準兒他們所使用的字元用這種方法轉換 就需要4個位元組。這樣問題又來了到底該使用幾個位元組表示一個字元呢?如果規定兩個位元組,有的字元會表示不出來,如果規定較多的位元組表示一個字元,很多人又 不答應,因為本來有些語言的字元兩個位元組處理就可以了,憑什麼用更多的位元組表示,多麼浪費。

這時就會想可不可以用變長的位元組來存儲一個字元呢?如果使用了變長的位元組表示一個字元,那就必須要知道是幾個位元組表示了一個字元,要不然計算機可沒 那麼聰明。下面介紹一下最常用的utf-8(utf是unicode transformation format的縮寫)的設計。請看下圖(來自阮一峰的部落格)

x表示可用的位

字元編碼淺談

通過utf-8的對應關系可以把每個字元在unicode中對應的碼點,轉換成相應的計算機的二進制表示。可以發現按照utf-8進行轉換是完全兼 容原先的ascii的;而且在多位元組表示一個字元時,開頭有幾個1就表示這個字元按照utf-8轉換後由幾個位元組表示。下面一個執行個體子來自阮一峰的部落格

已知“嚴”的unicode是4e25(100111000100101),根據上表,可以發現4e25處在第三行的範圍内(0000 0800-0000 ffff),是以“嚴”的utf-8編碼需要三個位元組,即格式是“1110xxxx 10xxxxxx 10xxxxxx”。然後,從“嚴”的最後一個二進制位開始,依次從後向前填入格式中的x,多出的位補0。這樣就得到了,“嚴”的utf-8編碼是 “11100100 10111000 10100101”,轉換成十六進制就是0xe4b8a5。

除了utf-8這種轉換方法,還存在utf-16,utf-32等等轉換方法。這裡就不再多做介紹。(注意utf後邊的數字代表的是碼元的大小。碼 元(code unit)是指一個已編碼的文本中具有最短的比特組合的單元。對于utf-8來說,碼元是8比特長;對于utf-16來說,碼元是16比特長。換一種說法 就是utf-8的是以一個位元組為最小機關的,utf-16是以兩個位元組為最小機關的。)

花了兩天時間終于寫完了,相信看到這裡大家對于字元編碼有了較為清楚的認識,當然文章中肯定存在不準确之處,希望大家批評指正。

郵箱:acmerfight圈gmail.com

<a href="https://zh.wikipedia.org/wiki/%e5%ad%97%e7%ac%a6%e7%bc%96%e7%a0%81" target="_blank">字元編碼</a>

<a href="http://www.joelonsoftware.com/articles/unicode.html" target="_blank">the absolute minimum every software developer absolutely, positively must know about unicode and character sets (no excuses!)</a>

<a href="http://www.ruanyifeng.com/blog/2007/10/ascii_unicode_and_utf-8.html" target="_blank">字元編碼筆記:ascii,unicode和utf-8</a>

<a href="http://www.cnblogs.com/skynet/archive/2011/05/03/2035105.html" target="_blank">字元集和字元編碼</a>

<a href="http://www.zhihu.com/question/20650946" target="_blank">windows 記事本的 ansi、unicode、utf-8 這三種編碼模式有什麼差別?</a>

<a href="http://www.zhihu.com/question/19943875" target="_blank">如何向非技術人員解釋 unicode 是什麼</a>

<b> 原文釋出時間為:2013-05-21</b>

<b>本文來自雲栖社群合作夥伴“linux中國”</b>