注:因爲兩邊同步的麻煩,更多更改及調整可參考個人網站:xiaogd.net 上的字符集編碼與亂碼系列,已將字符集編碼系列與亂碼探源系列合併,更新及勘誤等再也不更新到這邊。html
在深刻研究字符集編碼,簡稱編碼以前,咱們先引入一個概念:編號(code),引入它是爲了更好地與編碼(encode)相區分。網絡
若是你對Unicode有深刻了解,你也許已經意識到了Unicode中碼點(code point)扮演的正是編號的角色。相似的還有GB系列中所謂的區位碼。ide
其實叫什麼並不重要,愛咋咋地,我並不關心。但亂叫容易叫混了,好比把碼點也叫成Unicode編碼,這裏先把這些納入到編號概念。爲區別起見,用黑色加粗的編碼特指字符集編碼。網站
到了後面你甚至會爲字符集編碼的邊界在哪而困惑,爲它的準肯定義而糾結,不過到那時你已經屬於」可貴糊塗「了,編號這一律念你也能夠把它丟到爪哇國去了。編碼
編號是什麼?spa
編號能夠看做是字符與編碼中間的一個抽象層,過渡層:.net
廣義上說,編號也可當作是某種編碼;代理
狹義上說,編碼也可視做爲某種編號。code
圖:字符<-->編號<-->編碼orm
編號與編碼的主要區別在於編號不涉及具體使用多少字節來表示、是用定長仍是變長方案等細節問題。編號僅僅是一個抽象的概念,是把字符數字化的一個過程。
在進一步比較編號與編碼以前,咱們先看看編號是如何來的。
數學意義上的集合(Set):一組不重複的,無序的的元素。
通常意義上的字符集:不重複的,帶編號的有序的的字符集合。
圖:數學意義上的字符集對比實際的字符集
字符被整理出來以後每每數量衆多,一般是置於表格之中。出於統計方面的緣由,人們一般用一個數字來編號。表格自己就是一種有序的暗含了編號的形式,哪怕你沒有明確地爲其編號,人們拿到這個表格也會說:」嘿,第一個字符是XXX!「這裏的一不就是編號嗎?
能夠把整理的工做視做是由一羣徹底不懂計算機的語言文字學家來完成的,他們甚至連字節是什麼都沒據說過。所謂編號是抽象的就是說它僅僅是一個數字而已。
這裏不打算去討論哲學意義上的等同,你可能會碰到有些字符彼此間長得很是像,在有了編號後,咱們能夠簡單地說,只要是編號不一樣的兩個字符就是不重複的。
圖:一些很類似的字符,圖片來自http://wiki.secondlife.com/wiki/Unicode_In_5_Minutes
這裏的U+[XX]XXXX是碼點的表示形式,X表明一個十六制數字,能夠有4-6位,不足4位前補0補足4位,超過則按是幾位就是幾位。具體範圍是U+0000~U+10FFFF。
注意不要將它與UTF-16的編碼搞混了,尤爲是那些4位的碼點,雖然很類似,但一個是編號(code),一個是編碼(encode),處在不一樣的概念層次。
關於碼點及Unicode的更詳細介紹,可見字符集與編碼(四)——Unicode
咱們能夠看到至少有三個碼點上的a是很是像的。其中的U+FF41中所謂的FULLWIDTH(全寬)其實就是全角,也即兩個半角寬度,我想咱們對此都很熟悉。
當初GB2312出來時,仗着本身編碼空間大,把ASCII裏那些字母符號之類的又重複弄出一套所謂的全角版原本,後來Unicode又把這些又再收羅了過去。
不必定!它能夠是數字對,或者你叫它複數,二元數啥的,隨便你。但只要它是離散可數量子化的,它天然也能夠轉換成惟一的一個數字。參見前面圖中的二維區位編號,咱們用數字對(1, 1)編號「h」這個字符。(1, 1)能夠簡單轉換成11,而後能夠進一步映射到從0或者1開始的編號。
有序不意味着連續。這裏須要說明的一點是,從前面的敘述來看,編號早於編碼,但實際狀況是人們一般是一塊兒考慮這二者的,編號反過來會受到編碼考慮的影響,這樣作只是爲了讓從編號到編碼的映射或者叫轉換更加方便。這些影響包括:
若是按平常習慣,編號一般應該從1開始,受編碼影響,編號也從0開始。
編號寫成十進制是更天然的方式,受編碼影響,編號一般也以十六進制形式來書寫,並寫成固定的位數,不夠時就在前面填充0,好比把48寫成0048;又好比:U+1D11E就是一個一個五位的編號。
爲了之後的擴展方便,編碼經常會跳過某些碼位,甚至會保留大片的區域未定義或做保留用途。好比Unicode有所謂的代理區(surrogate area),後續咱們會進一步瞭解。編號所以也跳過這些。(其實到了後面你會發現,究竟誰影響誰還真很差說!不過等你明白之時這些已經不重要了。。。)
總之一句話就是讓映射規則儘量簡單。
圖:編號最終與編碼幾乎同樣的一種可能情形,爲簡單起見,使用十進制。
你可能會說,那這樣它們還有什麼區別?你的確能夠把編號也說成是編碼。
但事情並不老是這樣,這種類似性確實迷惑了不少人,特別是Unicode,不少人把碼點說成是Unicode編碼,這種說法自己並無錯,這取決於你如何定義編碼,但他們是否意識碼點僅僅一種抽象的編碼呢?爲了區別,Unicode把最終的具體編碼稱爲UTF(Unicode Transformation Format),即所謂的Unicode轉換格式。
所謂轉換,其實就是把抽象的數字映射到具體的,最終的編碼上來。
把一個字符編碼到一個數字(不涉及每一個數字用幾個字節表示,是用定長仍是變長表示等具體細節)
即UTF,把抽象編碼層面的數字編碼成最終的存儲形式,須要明確是用定長仍是變長;定長的話定幾個字節;用變長的話有哪幾種字節長度,具體如何去實現等等。(注:在上一層面,字符與數字已經實現一一對應,對數字編碼實質就是對字符編碼)
這裏所謂抽象與具體,以U+0061(ascii字母a)爲例,十六進制的0061也就是十進制的97,所謂抽象,也便是用97這個數字表示a;所謂具體,就是在計算機的底層到底怎麼表示的問題。即使是表示一個整數,你也面臨着究竟是用byte,short,仍是int,long來表示的問題,這就是具體。更具體到編碼,你還面臨是用定長仍是變長等抉擇。以UTF-32爲例,本質上與一個四字節的unsigned int(無符號整型)沒什麼區別。
編碼是一個很是寬泛的概念!雖然咱們前面一直用編碼特指字符集編碼,但這只是一種狹義的理解,廣義的理解則有不少:
文字是對聲音的編碼
照相機,攝像機把光信號編碼成圖像及視頻
咱們還常常能看到條形碼,二維碼,這些都是編碼
在《編碼:隱匿在計算機軟硬件背後的語言》(Code: The Hidden Language of Computer Hardware and Software)一書中,參見豆瓣讀書http://book.douban.com/subject/4822685/,做者提到了莫爾斯電碼(Morse Code)
以及布萊葉盲文(Braille Code),這些都是編碼的例子。
在《信息簡史》( The Information: A History, A Theory, A Flood)一書中,參見豆瓣讀書http://book.douban.com/subject/25752043/,做者提到了一個有趣的「會說話的鼓」的故事,非洲的一些部落成員之間能夠用鼓聲來交流很是複雜的訊息,在這裏就是用鼓聲來編碼信息。
電影《修女傳》中有這樣的情形,當修女們還坐着船在剛果河上行進時,船上的鼓手們就提早用鼓聲告訴遠方的目的地村莊,「來了一位美麗的修女」(由奧黛麗·赫本(Audrey Hepburn)主演)。聲音的速度畢竟比船快!
回到咱們的字符集的例子,雖然咱們傾向於認爲編碼就是指最終存儲的形式,好比寫入文件時或者放在內存時,又或者是在網絡傳輸的過程當中。
但若是咱們要說,字符集編碼這一律念也能夠包含抽象層面的編碼,那麼這樣一種說法也並沒有不妥,只要你能準確區分這兩個層面,你怎麼去看待它們都是能夠的,仍是前面那句話,這取決於你如何定義編碼,好比咱們能夠說GBK中的區位碼難道不是字符集編碼嗎?這就取決於你如何看待GBK編碼這一律唸了,是狹義的去看待仍是廣義的去看待。
我不想爲字符集編碼的準肯定義去爭論,在我看來,當你說編碼時,只要你本身清楚說的是哪一個層面就ok了,咱們在前面引入了編號,目的是爲在還沒有澄清以前做更好的區分,若是你如今已經清楚了,就能夠把編號丟掉了。
事實上,當人們說到Unicode編碼時,更常是指它的抽象編碼層,即碼點這一層面,實際上這纔是Unicode的核心所在。
Unicode的核心就是爲每一個字符提供惟一一個數字編號。Unicode provides a unique number for every character.
讓咱們來看一個更加具體的示意圖:
關於碼點如何具體轉換成各類編碼,這個在後面再做討論。從圖上咱們能夠初步得出一些結論。好比
UTF-8與UTF-16都是變長編碼,UTF-32則是定長編碼。
碼點到UTF-32的轉換最簡單,就是在前面墊0墊夠4字節就好了。
碼點到UTF-8的轉換,除了最小那個在數值上同樣外,其它兩個徹底看不出二者的關係。
碼點到UTF-16的轉換則是最微妙的,能夠看出前兩個字符UTF-16與碼點是徹底一致的,但那個大碼點(準確地說是超過了U+FFFF的碼點)則有了很大的變化,長度變成了四字節,值也變得很不同了。
關於UTF-16的誤解是不少的,部分可能因爲它的名字上帶了個16,讓人誤覺得它是16位定長的兩字節編碼。但正像UTF-8並非僅僅是8位同樣,UTF-16也不只僅是16位。
事實上,UTF-16的前身UCS-2確實是16位定長的編碼,它跟碼點在形式上就是徹底同樣了,實際我很懷疑那時候壓根就沒碼點這一說法,那時人們甚至也不說UCS-2,直接就叫Unicode!
時至今天,你依然能夠在很多地方看到把UTF-16寫成Unicode的,而後與UTF-8並排在一塊兒,顯得不三不四的,固然了,這是有歷史緣由的。
簡單地說,字符擴充了,目前碼點的範圍是U+0000~U+10FFFF。U+10FFFF是多大呢?大概是111萬。
按Unicode官方的說法,就這樣了,之後也不擴充了,一百多萬足夠用了,目前也只是定義了10萬多個字符左右。
而16位定長的話,撐死了也就6萬多,因此不變就不行了。
在後面的篇章中,將進一步分析定長,變長的問題。