關於二進制編碼的那些事
字符編碼常用類型介紹
常用編碼介紹一覽表
第一個:ASCII
ASCII是美國基於拉丁字母的一套編碼系統,主要是顯示現代的英語以及其他的西歐語言。它是現在最通用的單字節編碼系統,並等同於國際標准ISO/IEC 646,如下圖所示:
因為,計算機是美國發明的,因此,最早只有127個字符編入被編碼到計算機系統,也就是大小寫字母,數字以及一些符號,這個編碼表被稱為ASCII編碼,比如大寫字母A是65,小寫字母a是97位。后127位是擴展ASCII碼。
在這里,每一位0或者1所占的空間單位被稱為bit(比特),這是計算機最小的單位,每8個bit組成一個字符,這是計算機最小的存儲單元。
常見單位換算:
bit位,計算機中最小的表示單位
8bit=1bytes,字節,計算機最小的存儲單元,1Bytes縮寫成1B
1KB=1024b
1MB=1024kb
1GB=1024MB
1TB=1024GB
第二個:GB2312以及GBK
對於我們來說,計算機顯示中文是尤為的重要,而對於我們而言,ASCII表里連一個偏旁部首都沒有。所以我們還需要一張關於中文和數字對應的關系表。一個字節最多能表示256個字符,要處理中文,顯然一個字節是不夠用的,所以我們采用兩個字節來表示,而且還不能與英文的ASCII編碼沖突,中國定制了GB2312編碼,用來把中文編碼進去。
第三個:Unicode
但長此以往,每個國家都會有自己的編碼標准, 就會造成比較混亂的局面,從而導致各個國家之間不能正常的通信,這個時候,有一個叫ISO的組織出現,編寫了一套Unicode的字符集,將所有國家的字符集都放了進去;它只規定了二進制代碼,但卻沒有規定二進制應該如何存儲。
比如嚴字這個字的Unicode碼是十六進制下的4E25,轉換成二進制足足有15位,需要兩個字節。表示更大的字節,可能需要3個字節甚至是4個字節。
這里就出現兩個嚴重的問題,第一個問題是:如何才能區分Unicode以及ASCII碼?計算機如何知道三個字節代表一個字符,而非三個字符;第二個問題,就算使用Unicode進行統一的編碼,而對於英文字符,僅僅需要一個字節就可以滿足條件,如果按照Unicode進行編碼的話,前面就會有大量的0,從而導致文件就會比以前的文件大兩倍到三倍,這是無法接受的;
其造成的結果就是:第一,出現了Unicode的多種存儲方式,也就是說有許多中不同的二進制格式,可以表示Unicode;第二,Unicode在很長一段的時間無法推廣,直到互聯網的出現。
第四個:UTF-8
互聯網的出現,強烈要求出現統一的編碼方式。UTF-8就是在互聯網上使用最廣的Unicode的實現方式。其他的實現方法還有UTF-16(字符用兩個字節或者四個字節表示)和UTF-32(字符用四個字節表示),不過互聯網基本不用。重復強調一下,UTF-8是Unicode的實現方式之一。
UTF-8最大的特點就是其是一種變長的編碼方式。它可以使用1~4個字節表示一個符號,根據不同的符號而變化字節長度。
UTF-8的編碼規則相當的簡單,只有兩條:
- 對於單字節的字符,字節的第一位設為0,后面的7位為這個符號的Unicode碼。因此對於英文字母,UTF-8與ASCII碼都是相同的。
- 對於n字節的字符(n>1),第一個字節的前n位都設為1,第n+1位設為0,后面字節的前兩位都設為10,。剩下的沒有提及的字節的二進制位,全部設置為Unicode碼。
下面總結了編碼規律,字母x表示可用的編碼的位
Unicode符號范圍 | UTF-8編碼方式 (十六進制) | (二進制) --------------------+--------------------------------------------- 0000 0000-0000 007F | 0xxxxxxx 0000 0080-0000 07FF | 110xxxxx 10xxxxxx 0000 0800-0000 FFFF | 1110xxxx 10xxxxxx 10xxxxxx 0001 0000-0010 FFFF | 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
根據上表,解讀UTF-8編碼十分的簡單。如果一個字節的第一位是0,則這個字節單獨就是一個字符;如果第一位是1,則連續有多少個1,則表示當前字符占有多少個字節。
下面還是以漢字“嚴”為例子,演示如何實現UTF-8編碼。
已經知道UTF-8的編碼中“嚴”是4E25(100111000100101),根據上表,可以發現出於第三行的范圍內(0000 0800-0000 FFFF),因此,“嚴”的UTF-8編碼需要三個字節,即格式是“1110xxxx 10xxxxxx 10xxxxxx”。然后,從“嚴”最后一個二進制開始,依次從后向前填入格式中的x,多出來的位就補0。這樣就得到了,“嚴”的UTF-8編碼是“11100100 10111000 10100101”,轉化成十六進制就是E4B8A5。
第五個:關於Unicode與UTF-8的轉換
通過上面的例子。我們可以看出,可以看出“嚴”字的Unicode編碼是4E25,UTF-8的編碼是E4B8A5,兩種是不一樣的,他們兩者之間的轉化是通過程序進行轉換的。
在window平台上,有一個最簡單的轉換辦法,就是使用內置的記事本小程序Notepad++.exe。打開文件后,點擊“文件”菜單中的“另存為”命令,會跳出一個對話框,在底部會有一個編碼的下拉條:
里面會有四個選項:ASCII、Unicode、Unicode big endian、UTF-8
- ASCII是默認編寫方式,對於英文文件是ASCII編碼,對於簡體中文是GB2312編碼,(只針對window系統下的簡體中文版,對於繁體中文版會采用BIG5碼)。
- Unicode編碼指的是USC-2編碼方式,即直接用兩個字符存入字符的的Unicode編碼,這個選項用的是little endian格式。
- UTF-8也就是上節所說的格式
選擇完“編碼方式”后,點擊“保存”按鈕,文件的編碼方式就立刻轉換好了。
7.Little endian和Big endian
上一節提到,UTF-8可通過USC-2格式直接存儲。以漢字“嚴”為例,Unicode碼是4E25,需要兩個字節進行存儲,一個字節是4E,另外一個字節是25,存儲的時候。4E在前,25在后,就是Big endian的方式;而25在前4E在后,就是Little endian的方式。
這兩個古怪的名稱來自於英國作家斯威夫特的《格列夫游記》。在該書中,小人國內爆發了內亂,戰爭起因是人們的爭論,吃雞蛋時是從大頭敲開(BIg-endian)還是從小頭(Little-endian)敲開。為了這個事件,前后爆發了六次戰爭,一個國王喪命了,另外一個國王丟了王位。
因此,第一個字節在前,就是“大頭方式”(Big endian),第二個字節在前,就是“小頭方式”(Little endian)。
那么很自然的就出現了一個問題:計算機如何知道你采用哪一種方式編碼?
Unicode規范中定義,每一個文件最前面加上表示編碼順序的字符,這個字符的名字叫做“零寬度非換行空格”(ZERO WIDTH NO-BREAK SPACE),用FEFF表示。這正好是兩個字節,而且FF比FE大1,。
如果一個文本頭兩個字節是FE FF,就表示該文件采用的是大頭方式;如果頭兩個字節是FF FE,就表示該文件采用的是小頭方式。
8.示例
下面舉一個示例。
打開“記事本”程序Notepad.exe,新建一個文本文件,內容就是一個“嚴”字,依次采用ASCII碼,Unicode,Unicode Big endian和UTF-8編碼方式保存。
然后,用文本編輯軟件UltraEdit中的“十六進制功能”,觀察該文件的內部編碼方式。
- ASCII碼 :文件編碼就是兩個字節“D1 CF”,這正是“嚴”的GB2312編碼,這樣按時GB2312是按照大頭編寫的。
- Unicode碼:編碼是四個字節“FF FE 25 4E”,其中FF FE暗示着是小頭編寫,真正的編碼是4E 25。
- Unicode Big endian: 編碼是四個字節“FE FF 4E 25”,其中FE FF表示大頭編寫。
- UTF-8編寫是六個字節“EF BB BF E4 B8 A5”,前三個表示這是UTF-8編碼,后三個表示的是UTF-8的編碼,它的存儲順序與編碼順序是一致的。
9. 關於在修改以及保存、傳輸等方面文件編碼關系
在計算機內存中,統一使用Unicode編碼,當需要保存到硬盤或者傳輸的時候,被編寫為UTF-8編碼。
用記事本編輯的時候,從文件讀取的UTF-8被轉化成Unicode內存中,編輯完成以后,保存的文件再把Unicode轉化成UTF-8保存到文件中。
瀏覽網頁的時候,服務器會把動態生成的Unicode轉換成UTF-8再傳輸到互聯網上面:
9.關於encode以及decode
decode的作用是把其他編碼的字符串轉化成Unicode編碼,如str1.decode(“GBK”),表示將GBK的字符串轉化成Unicode編碼
encode的作用是把Unicode轉化成其他編碼的字符串,如str2.encode("GBK"),表示將Unicode編碼轉化成GBK格式。