字符編碼是計算機編程中不可回避的問題,不管你用 Python2 還是 Python3,亦或是 C++, Java 等,我都覺得非常有必要厘清計算機中的字符編碼概念。本文主要分以下幾個部分介紹:
- 基本概念
- 常見字符編碼簡介
- Python 的默認編碼
- Python2 中的字符類型
- UnicodeEncodeError & UnicodeDecodeError 根源
基本概念
字符(Character)
在電腦和電信領域中,字符是一個信息單位,它是各種文字和符號的總稱,包括各國家文字、標點符號、圖形符號、數字等。比如,一個漢字,一個英文字母,一個標點符號等都是一個字符。字符集(Character set)
字符集是字符的集合。字符集的種類較多,每個字符集包含的字符個數也不同。比如,常見的字符集有 ASCII 字符集、GB2312 字符集、Unicode 字符集等,其中,ASCII 字符集共有 128 個字符,包含可顯示字符(比如英文大小寫字符、阿拉伯數字)和控制字符(比如空格鍵、回車鍵);GB2312 字符集是中國國家標準的簡體中文字符集,包含簡化漢字、一般符號、數字等;Unicode 字符集則包含了世界各國語言中使用到的所有字符字符編碼(Character encoding)
字符編碼,是指對于字符集中的字符,將其編碼為特定的二進制數,以便計算機處理。常見的字符編碼有 ASCII 編碼,UTF-8 編碼,GBK 編碼等。一般而言,字符集和字符編碼往往被認為是同義的概念,比如,對于字符集 ASCII,它除了有「字符的集合」這層含義外,同時也包含了「編碼」的含義,也就是說,ASCII 既表示了字符集也表示了對應的字符編碼。
常見字符編碼簡介
常見的字符編碼有 ASCII 編碼,GBK 編碼,Unicode 編碼和 UTF-8 編碼等等。這里,我們主要介紹 ASCII、Unicode 和 UTF-8。
ASCII
計算機是在美國誕生的,人家用的是英語,而在英語的世界里,不過就是英文字母,數字和一些普通符號的組合而已。
在 20 世紀 60 年代,美國制定了一套字符編碼方案,規定了英文字母,數字和一些普通符號跟二進制的轉換關系,被稱為 ASCII (American Standard Code for Information Interchange,美國信息互換標準編碼) 碼。
比如,大寫英文字母 A 的二進制表示是 01000001(十進制 65),小寫英文字母 a 的二進制表示是 01100001 (十進制 97),空格 SPACE 的二進制表示是 00100000(十進制 32)。Unicode
ASCII 碼只規定了 128 個字符的編碼,這在美國是夠用的。可是,計算機后來傳到了歐洲,亞洲,乃至世界各地,而世界各國的語言幾乎是完全不一樣的,用 ASCII 碼來表示其他語言是遠遠不夠的,所以,不同的國家和地區又制定了自己的編碼方案,比如中國大陸的 GB2312 編碼 和 GBK 編碼等,日本的 Shift_JIS 編碼等等。
雖然各個國家和地區可以制定自己的編碼方案,但不同國家和地區的計算機在數據傳輸的過程中就會出現各種各樣的亂碼(mojibake),這無疑是個災難。
怎么辦?想法也很簡單,就是將全世界所有的語言統一成一套編碼方案,這套編碼方案就叫 Unicode,它為每種語言的每個字符設定了獨一無二的二進制編碼,這樣就可以跨語言,跨平臺進行文本處理了,是不是很棒!
Unicode 1.0 版誕生于 1991 年 10 月,至今它仍在不斷增修,每個新版本都會加入更多新的字符,目前最新的版本為 2016 年 6 月 21 日公布的 9.0.0。
Unicode 標準使用十六進制數字,而且在數字前面加上前綴 U+
,比如,大寫字母「A」的 unicode 編碼為 U+0041
,漢字「嚴」的 unicode 編碼為 U+4E25
。更多的符號對應表,可以查詢 unicode.org,或者專門的漢字對應表。UTF-8
Unicode 看起來已經很完美了,實現了大一統。但是,Unicode 卻存在一個很大的問題:資源浪費。
為什么這么說呢?原來,Unicode 為了能表示世界各國所有文字,一開始用兩個字節,后來發現兩個字節不夠用,又用了四個字節。比如,漢字「嚴」的 unicode 編碼是十六進制數 4E25
,轉換成二進制有十五位,即 100111000100101,因此至少需要兩個字節才能表示這個漢字,但是對于其他的字符,就可能需要三個或四個字節,甚至更多。
這時,問題就來了,如果以前的 ASCII 字符集也用這種方式來表示,那豈不是很浪費存儲空間。比如,大寫字母「A」的二進制編碼為 01000001,它只需要一個字節就夠了,如果 unicode 統一使用三個字節或四個字節來表示字符,那「A」的二進制編碼的前面幾個字節就都是 0,這是很浪費存儲空間的。
為了解決這個問題,在 Unicode 的基礎上,人們實現了 UTF-16, UTF-32 和 UTF-8。下面只說一下 UTF-8。
UTF-8 (8-bit Unicode Transformation Format) 是一種針對 Unicode 的可變長度字符編碼,它使用一到四個字節來表示字符,例如,ASCII 字符繼續使用一個字節編碼,阿拉伯文、希臘文等使用兩個字節編碼,常用漢字使用三個字節編碼,等等。
因此,我們說,UTF-8 是 Unicode 的實現方式之一,其他實現方式還包括 UTF-16(字符用兩個或四個字節表示)和 UTF-32(字符用四個字節表示)。
Python 的默認編碼
Python2 的默認編碼是 ascii,Python3 的默認編碼是 utf-8,可以通過下面的方式獲取:
Python2 中的字符類型
Python2 中有兩種和字符串相關的類型:str 和 unicode,它們的父類是 basestring。其中,str 類型的字符串有多種編碼方式,默認是 ascii,還有 gbk,utf-8 等,unicode 類型的字符串使用 u'...' 的形式來表示,下面的圖展示了 str 和 unicode 之間的關系:
UnicodeEncodeError & UnicodeDecodeError 根源
Ref:http://funhacks.net/explore-python/Basic/character_encoding.html