字符編碼

從網上轉載一篇文章,原連接爲https://www.cnblogs.com/leesf456/p/5317574.htmlphp

原文以下:html

////////////////////////////////////////////////////////////////////windows

1、前言網絡

  在解決昨天的問題時,又引出了不少新的問題,如爲何要進行編碼,這些編碼的關係如何,如ASCIIIOS-8859-1GB2312GBKUnicode之間的關係,筆者想要完全理解字符編碼背後的故事,遂進行了探索,具體筆記以下。如園友能讀完本篇文章,我相信會解開不少疑惑。學習

2、字符編碼編碼

  2.1 爲什麼須要編碼?spa

  咱們知道,全部的信息最終都表示爲一個二進制的字符串,每個二進制位(bit)有01兩種狀態。當咱們須要把字符'A'存入計算機時,應該對應哪一種狀態呢,存儲時,咱們能夠將字符'A'01000010(這個隨便編的)二進制字符串表示,存入計算機;讀取時,再將01000010還原成字符'A'。那麼問題來了,存儲時,字符'A'應該對應哪一串二進制數呢,是01000010?或者是10000000 11110101?說白了,就是須要一個規則。這個規則能夠將字符映射到惟一一種狀態(二進制字符串),這就是編碼。而最先出現的編碼規則就是ASCII編碼,在ASCII編碼規則中,字符'A'既不對應01000010,也不對應1000 0000 11110101,而是對應01000001(不要問爲何,這是規則)。代理

  2.2 ASCIIcode

  這套編碼規則是由美國定製,一共規定了128個字符的編碼,好比空格"SPACE"32(十進制)(二進制00100000),大寫的字母A65(二進制01000001)。這128個符號(包括 32個不能打印出來的控制符號),只佔用了一個字節(8 bit)的後面7位,最前面的1位統一規定爲0。總共纔有128個字符編碼,一個字節都沒有用完,這好像彷佛有點太少了。因而乎,就開始壓榨最高位,對其爲1時也進行編碼,利用最高位進行編碼的方式就稱爲非ASCII編碼,如ISO-8859-1編碼。orm

  2.3 ISO-8859-1

  這套編碼規則由ISO組織制定。是在 ASCII 碼基礎上又制定了一些標準用來擴展ASCII編碼,即 000000000~ 01111111127) 與ASCII的編碼同樣,對 10000000128~ 11111111255)這一段進行了編碼,如將字符§編碼成 10100111167)。ISO-8859-1編碼也是單字節編碼,最多可以表示256個字符Latin1ISO-8859-1的別名,有些環境下寫做Latin-1。可是,即便可以表示256個字符,對中文而言,仍是太少了,一個字節確定不夠,必須用多個字節表示。可是,因爲是單字節編碼,和計算機最基礎的表示單位一致,因此不少時候,仍舊使用 ISO8859-1編碼來表示。並且在不少協議上,默認使用該編碼。好比,雖然"中文"兩個字不存在ISO8859-1編碼,以GB2312編碼爲例,應該是D6D0 CEC4兩個字符,使用ISO8859-1編碼的時候則將它拆開爲4個字節來表示:D6D0 CEC4(事實上,在進行存儲的時候,也是以字節爲單位進行處理)。而若是是UTF編碼,則是6個字節e4 b8 ad e6 96 87。很明顯,這種表示方法還須要以另外一種編碼爲基礎才能正確顯示。而常見的中文編碼方式有GB2312BIG5GBK

  2.4 GB2312

  GB2312其對所收錄字符進行了"分區"處理,共94個區,區從1(十進制)開始,一直到94(十進制),每區含有94個位,位從1(十進制)開始,一直到94(十進制),共883694 * 94)個碼位,這種表示方式也稱爲區位碼,GB2312是雙字節編碼,其中高字節表示區,低字節表示位。各區具體說明以下:

01-09區收錄除漢字外的682個字符,有164個空位(9 * 94 - 682)。10-15區爲空白區,沒有使用。16-55區收錄3755個一級漢字(簡體),按拼音排序。

56-87區收錄3008個二級漢字(簡體),按部首/筆畫排序。88-94區爲空白區,沒有使用。

  那麼根據區位碼如何算出GBK2312編碼呢?區位碼的表示範圍爲0101 - 9494(包含了空的區位碼)。點擊這裏,查看中GB2312編碼區位碼。以後只須要按照以下規則進行轉化便可。

  1. 將區(十進制)轉化爲十六進制。

  2. 將轉化的十六進制加上A0,獲得GB2312編碼的高字節。

  3. 將位(十進制)轉化爲十六進制。

  4. 將轉化的十六進制加上A0,獲得GB2312編碼的低字節。

  5. 組合區和位,區在高字節,位在低字節。

  6. 獲得GB2312編碼。

  具體的流程圖以下:

  例如:''字的區位碼爲3278(表示在32區,78位)。1. 32(區)轉化爲十六進制爲202. 加上A0C03. 78(位)轉化爲十六進制爲4E4. 加上A0EE5. 組合區和位,爲C0EE6. 獲得GB2312編碼,即''字的GB2312編碼爲C0EE

  GB2312用兩個字節編碼,採用分區編碼,總共編碼的中文個數爲67633755 + 3008)。這些漢字只是最經常使用的漢字,已經覆蓋中國大陸99.75%的使用頻率。可是,還有一些漢字在GB2312中沒有被編碼,如''字,在GB2312中就沒有被編碼,這樣就致使了問題,隨之就出現了主流的GBK編碼。在講解GBK編碼以前,咱們另外講解一下BIG5編碼。

  2.5 BIG5

  BIG5採用雙字節編碼,使用兩個字節來表示一個字符。高位字節使用了0x81-0xFE,低位字節使用了0x40-0x7E,及0xA1-0xFE。該編碼是繁體中文字符集編碼標準,共收錄13060箇中文字,其中有二字爲重複編碼,即兀、兀A461C94A)嗀、嗀」(DCD1DDFC)。具體的分區以下:  

 

8140-A0FE 保留給使用者自定義字符(造字區)

A140-A3BF 標點符號、希臘字母及特殊符號。其中在A259-A261,收錄了度量衡單位用字:兙兛兞兝兡兣嗧瓩糎。

A3C0-A3FE 保留。此區沒有開放做造字區用。A440-C67E 經常使用漢字,先按筆劃再按部首排序。

C6A1-F9DC 其它漢字。

F9DD-F9FE 製表符。

 

  點擊這裏,查看BIG5編碼。注意,BIG5編碼與GBK編碼沒有什麼關係。

  2.6 GBK

  GBK編碼擴展了GB2312,徹底兼容GB2312編碼(如''字的GBKGB2312編碼均爲C0EE),但其不兼容BIG5編碼(''字的BIG5編碼爲AAF8GBK編碼爲E94C''字的BIG5編碼爲A7F5 不等於C0EE),即若是使用GB2312編碼,使用GBK解碼是徹底正常的,可是若是使用BIG5編碼,使用GBK解碼,會出現亂碼。相比於GB2312編碼,GBK編碼了更多漢字,如''字。GBK編碼依然採用雙字節編碼方案,其編碼範圍:8140FEFE,剔除xx7F碼位,共23940個碼位。能表示 21003 個漢字。點擊這裏,查看GBK編碼。點擊這裏,能夠查詢中文的其餘編碼。在GBK以後又出現了GB18030編碼,可是沒有造成主流,故不作介紹,至此,中文編碼的問題已經講解完成。那麼問題又來了,大陸網民與在海峽兩岸網民交流時,若都使用GBK編碼,則沒有問題,若一方使用GBK編碼,一方使用BIG5編碼,那麼就會出現亂碼問題,這是在海峽兩岸網民交流,若是漂洋過海進行交流呢?那就更容易出現亂碼問題,這時候咱們可能想,要是有一套全世界都通用的編碼就行了,不要擔憂,這樣的編碼確實是存在的,那就是Unicode

  2.7 Unicode

  有兩個獨立的, 創立單一字符集的嘗試. 一個是國際標準化組織(ISO)ISO 10646 項目, 另外一個是由多語言軟件製造商組成的協會組織的 Unicode 項目. 1991年先後, 兩個項目的參與者都認識到, 世界不須要兩個不一樣的單一字符集. 它們合併雙方的工做成果, 併爲創立一個單一編碼表而協同工做. 兩個項目仍都存在並獨立地公佈各自的標準, Unicode 協會和 ISO/IEC JTC1/SC2 都贊成保持 Unicode ISO 10646 標準的碼錶兼容, 並緊密地共同調整任何將來的擴展。

  Unicode是指一張表,裏面包含了可能出現的全部字符,每一個字符對應一個數字,這個數字稱爲碼點(Code Point),如字符'H'的碼點爲72(十進制),字符''的碼點爲26446(十進制)。Unicode表包含了1114112個碼點,即從000000(十六進制) - 10FFFF(十六進制)。地球上全部字符均可以在Unicode表中找到對應的惟一碼點。點擊這裏,查詢字符對應的碼點。Unicode將碼空間劃分爲17個平面,從00 - 10(十六進制,最高兩位),即從0 - 16(十進制),每一個平面有65536個碼點(2^16其中最重要的是第一個Unicode平面(碼位從0000 - FFFF),包含了最經常使用的字符,該平面被稱爲基本多語言平面(Basic Multilingual Plane),縮寫爲BMP,其餘平面稱爲輔助平面(Supplementary Planes),在基本多文種平面內, 從D800DFFF之間的碼位區段是永久保留不映射到字符的, 所以UTF-16編碼巧妙的利用了這保留下來的碼位來對輔助平面內的字符進行編碼,這點後面進行講解。Unicode只是一個符號集,只規定的字符所對應的碼點,並無指定如何存儲,如何進行存儲出現了不一樣的編碼方案,關於Unicode編碼方案主要有兩條主線:UCSUTFUTF主線由Unicode Consortium進行維護管理,UCS主線由ISO/IEC進行維護管理。

  2.8 UCS

  UCS全稱爲"Universal Character Set",在UCS中主要有UCS-2UCS-4

  1. UCS-2

  UCS-2是定長字節的,固定使用2個字節進行編碼,從0000(十六進制)- FFFF(十六進制)的碼位範圍,對應第一個Unicode平面。採用BOM(Byte Order Mark)機制,該機制做用以下:1. 肯定字節流採用的是大端序仍是小端序。2. 肯定字節流的Unicode編碼方案。

  2. UCS-4

  UCS-4是定長字節的,固定使用4個字節進行編碼。也採用了BOM機制。

  2.9 UTF

  UTF全稱爲"Unicode Transformation Format",在UTF中主要有UTF-8UTF-16UTF-32

  1. UTF-8

  UTF-8是一種變長編碼方式,使用1-4個字節進行編碼。UTF-8徹底兼容ASCII,對於ASCII中的字符,UTF-8採用的編碼值跟ASCII徹底一致。UTF-8Unicode一種具體的編碼實現UTF-8是在互聯網上使用最廣的一種Unicode的編碼規則,由於這種編碼有利於節約網絡流量(由於變長編碼,而非統一長度編碼)。關於Unicode碼點如何轉化爲UTF-8編碼,能夠參照以下規則:

  對於單字節的符號,字節的第一位設爲0,後面7位爲這個符號的unicode碼。所以對於英語字母,UTF-8編碼和ASCII碼是相同的。

  對於n字節的符號(n>1),第一個字節的前n位都設爲1,第n+1位設爲0,後面字節的前兩位一概設爲10。剩下的沒有說起的二進制位,所有爲這個符號的unicode碼。

  總結的編碼規則以下:

 

      Unicode符號範圍                   |   UTF-8編碼方式

         (十六進制) (十進制)            |   (二進制)

  ----------------------------------------------------------------------------------------------------

    0000 0000-0000 007F (0-127)           |    0xxxxxxx

    0000 0080-0000 07FF (128-2047)        |    110xxxxx 10xxxxxx

    0000 0800-0000 FFFF (2048-65535)      |     1110xxxx 10xxxxxx 10xxxxxx

    0001 0000-0010 FFFF (65536-1114111)   |    11110xxx 10xxxxxx 10xxxxxx 10xxxxxx

 

  說明:字符'A'Unicode碼點爲65(十進制),根據上表,在第一行範圍,則字符'A'UTF-8編碼爲01000001,中文字符''Unicode碼點爲26446(十進制),二進制爲01100111 01001110,十六進制爲674E。根據上表,在第三行範圍,則將''二進制代碼從低位到高位依次填入x中,不足的填入0。獲得UTF-8編碼爲11100110 10011101 10001110,即E69D8E(十六進制)。

  由上述編碼規則可知,0000 0000 - 0000 FFFF(第一行到第三行)爲Unicode第一個平面(基本多語言平面),而0001 0000 - 10 FFFF(第四行)爲Unicode其餘平面(輔助平面)。在基本多語言平面對應了絕大多數經常使用的字符。對於大於65535(十進制)的碼點,即在輔助平面上的碼點,須要使用4個字節來進行UTF-8編碼。

  2. UTF-16

  UTF-8是不定長的編碼,使用1234個字節編碼,而UTF-16則只使用24個字節編碼。UTF-16也是Unicode一種具體的編碼實現。關於Unicode如何轉化爲UTf-16編碼規則以下

  Unicode碼點在第一平面(BPM)中,則使用2個字節進行編碼。 

  Unicode碼點在其餘平面(輔助平面),則使用4個字節進行編碼。

  關於輔助平面的碼點編碼更詳細解析以下:輔助平面碼點被編碼爲一對16比特(四個字節)長的碼元, 稱之爲代理對(surrogate pair), 第一部分稱爲高位代理(high surrogate)或前導代理(lead surrogates),碼位範圍爲:D800-DBFF. 第二部分稱爲低位代理(low surrogate)或後尾代理(trail surrogates), 碼位範圍爲:DC00-DFFF。注意,高位代理的碼位從D800DBFF,而低位代理的碼位從DC00DFFF,總共剛好爲D800-DFFF,這部分碼點在第一平面內是保留的,不映射到任何字符,因此UTF-16編碼巧妙的利用了這點來進行碼點在輔助平面內的4字節編碼。

  說明:字符'A'Unicode碼點爲65(十進制),十六進制表示爲41,在第一平面。根據規則,UTF-16採用2個字節進行編碼。那麼問題又來了,知道了採用兩個字節編碼,而且咱們也知道計算機是以字節爲單位進行存儲,這兩個字節應該表示爲00 41(十六進制)?或者是41 00(十六進制)呢?這就引出了一個問題,須要用到以前說起的BOM機制來解決。

  表示爲00 41意味着採用了大端序(Big endian),而表示爲41 00意味着採用了小端序。那麼計算機如何知道存儲的字符信息採用了大端序仍是小端虛呢?這就須要加入一些控制信息,具體是採用大端序,則在文件前加入FE FF,採用小端序,則在文件前加入FF FE。這樣,當計算開始讀取時發現前兩個字節爲FE FF,就表示以後的信息採用的是小端序,反之,則是大端序。

  字符 (沒法顯示,只能截圖顯示),其Unicode碼點爲65902(十進制),十六進制爲1016E,很顯然,已經超出了第一平面(BMP)所能表示的範圍。其在輔助平面內,根據規則,UTF-16採用4個字節進行編碼。然而其編碼不是簡單擴展爲4個字節(00 01 01 6E),而是採用以下規則進行計算。

  使用Unicode碼位減去100000(十六進制),獲得的值擴展20位(由於Unicode最大爲10 FF FF(十六進制),減去1 00 00(十六進制)後,獲得的結果最大爲0FFF FF(十六進制),即爲20位,不足20位的,在高位加一個0,擴展至20位便可)。

  將步驟一獲得的20位,按照高十位和低十位進行分割。

  將步驟二的高十位擴展至2個字節,再加上D800(十六進制),獲得高位代理或前導代理。取值範圍是D800 - 0xDBFF

  將步驟二的低十位擴展至2個字節,再加上DC00(十六進制),獲得低位代理或後尾代理。取值範圍是DC00 - 0xDFFF

  UnicodeUTF-16規則流程圖以下:

 

 

  按照這個規則,咱們計算字符UTF-16編碼,咱們知道其碼點爲1016E,減去10000獲得016E,擴展至0016E,進行分割,獲得高十位爲00 0000 0000,十六進制爲0000,加上D800D800;獲得低十位爲01 0110 1110,十六進制爲016E,加上DC00DD6E;綜合獲得D8 00 DD 6E。即UTF-16編碼爲D8 00 DD 6E(也可爲D8 0 DD 6E)。

  而對於UTF-32是使用4個字節表示,也採用BOM機制,能夠類比UTF-16,這裏再也不額外介紹。

4、字符編碼區別

  4.1 UCS-2 UTF-16區別

  從上面的分析知道,UCS-2採用的兩個字節進行編碼。在0000FFFF的碼位範圍內,它和UTF-16基本一致,爲何說基本一致,由於在UTF-16中從U+D800U+DFFF的碼位不對應於任何字符,而在使用UCS-2的時代,U+D800U+DFFF內的值被佔用。

  UCS-2只能表示BMP內的碼點(只採用2個字節),而UTF-16能夠表示輔助平面內的碼點(採用4個字節)。

  咱們能夠抽象的認爲UTF-16可當作是UCS-2的父集。在沒有輔助平面字符(surrogate code points)前,UTF-16UCS-2所指的意思基本一致。但當引入輔助平面字符後,想要表示輔助平面字符時,就只能用UTF-16編碼了。

  4.2 UCS -4UTF-16的區別

  在BMP上,UTF-16採用2個字節表示,而在輔助平面上,UTF-16採用的是4個字節表示。對於UCS-4,無論在哪一個平面都採用的是四個字節表示。

  4.3 爲何UTF-8編碼不須要BOM機制

  由於在UTF-8編碼中,其自身已經帶了控制信息,如1110xxxx 10xxxxxx 10xxxxxx 10xxxxxx,其中1110就起到了控制做用,因此不須要額外的BOM機制。

5、總結

若是讀者有耐心看到這裏,我相信對於字符編碼這一塊已經就已經沒有什麼疑問了。寫到這裏,就完成了主流編碼的探索,探索的過程確實是不容易,最後弄清楚了,感受至關的快樂,也把經驗總結分享給各位園友,若是讀者有任何疑問,歡迎交流,謝謝各位園友的觀看~

 

//////////////////////////////////////////////////////////////////////////////////////////////////////////////////

學習總結:

ASCIIIOS-8859-1(Latin1)GB2312UnicodeGBKBIG5都是字符編碼的標準,ASCII是早期美國用於英文字符的編碼標準,IOS-8859-1ASCII碼基礎上擴充。對於中文編碼,有GB2312GBK擴展了GB2312BIG5是繁體中文的編碼標準。爲了統一各類編碼標準,推出Unicode,這是一種全世界通用的編碼標準,其實就是一張字符表,中英文,特殊符號均可以對應找到,可是unicode有一些問題,就是英文只用一個字節就夠了,而中文須要用兩個字節,更大的符號可能須要用34個字節,在具體存儲的時候,有兩個問題:

第一,我不知道幾個字節表示一個字符;第二,若是都用4個或2個字節表示,那麼對於英文來講存儲空間大大浪費。

因此,對於Unicode的具體實現推出了UTF-8,UTF-16,UTF-32。其中,UTF-8是變長字節編碼,解決了存儲空間浪費的問題。

這裏面其實還存在一個問題,就是咱們發現保存文件的時候,好比windows上的txt文件,能夠選擇UnicodeUTF-8,但UTF-8不就是Unicode的具體實現嗎?其實,這裏的Unicode也是具體指一種編碼實現。

對於常見的文件編碼四個選項:ANSIUnicodeUnicode big endian UTF-8

1ANSI是默認的編碼方式。對於英文文件是ASCII編碼,對於簡體中文文件是GB2312編碼(只針對Windows簡體中文版,若是是繁體中文版會採用Big5碼)。

2Unicode編碼指的是UCS-2編碼方式,即直接用兩個字節存入字符的Unicode碼。這個選項用的little endian格式。

3Unicode big endian編碼與上一個選項相對應。

4UTF-8編碼,變長字節的Unicode編碼實現。

相關文章
相關標籤/搜索
本站公眾號
   歡迎關注本站公眾號,獲取更多信息