主要內容:
前言:
學習Python,字符編碼間的轉換是繞不過去的一只攔路虎,不把編碼徹底搞明白,總有一天它會猝不及防坑你一把。
Python2.x和Python3.x在字符編碼的設置上也有很大區別(Python3未來將是主流,所以Python3為主),今天我們就來一起學習下。
上一篇文章里我已經簡述了Python的常見編碼了,這里就不再贅述了,還不清楚的小伙伴可以先去看下: http://www.cnblogs.com/schut/p/8406897.html
一、Unicode 和 UTF-8的愛恨糾葛
Unicode 起到了2個作用:
- 直接支持全球所有語言,每個國家都可以不用再使用自己之前的舊編碼了,用unicode就可以了。(就跟英語是全球統一語言一樣)
- unicode包含了跟全球所有國家編碼的映射關系。
Unicode解決了字符和二進制的對應關系,但是使用unicode表示一個字符,太浪費空間。
例如:利用unicode表示“Python”需要12個字節才能表示,比原來ASCII表示增加了1倍。
由於計算機的內存比較大,並且字符串在內容中表示時也不會特別大,所以內容可以使用unicode來處理,但是存儲和網絡傳輸時一般數據都會非常多,那么增加1倍將是無法容忍的!!!
為了解決存儲和網絡傳輸的問題,出現了Unicode Transformation Format,學術名UTF,即:對unicode中的進行轉換,以便於在存儲和網絡傳輸時可以節省空間!
UTF-8: 使用1、2、3、4個字節表示所有字符;優先使用1個字符、無法滿足則使增加一個字節,最多4個字節。英文占1個字節、歐洲語系占2個、東亞語系占3個,其它及特殊字符占4個。
UTF-16: 使用2、4個字節表示所有字符;優先使用2個字節,否則使用4個字節表示。
UTF-32: 使用4個字節表示所有字符。
總結:UTF 是為unicode編碼 設計 的一種在存儲和傳輸時節省空間的編碼方案。
二、字符在硬盤上的存儲
首先要明確的一點就是,無論以什么編碼在內存里顯示字符,存到硬盤上都是2進制(0b是說明這段數字是二進制,0x表示是16進制。0x幾乎所有的編譯器都支持,而支持0b的並不多)。理解這一點很重要。
比如:
ascii編碼(美國): l 0b1101100 o 0b1101111 v 0b1110110 e 0b1100101 GBK編碼(中國): 老 0b11000000 0b11001111 男 0b11000100 0b11010000 孩 0b10111010 0b10100010
還要注意的一點是:
存到硬盤上時是以何種編碼存的,再從硬盤上讀出來時,
就必須以何種編碼讀(開頭聲明或轉換),要不然就亂了。
三、編碼的轉換
雖然有了unicode and utf-8 ,但是由於歷史問題,各個國家依然在大量使用自己的編碼,
比如中國的windows,默認編碼依然是gbk,而不是utf-8。
基於此,如果中國的軟件出口到美國,在美國人的電腦上就會顯示亂碼,因為他們沒有gbk編碼。
所以該怎么辦呢?
還記得我們講unicode其中一個功能是其包含了跟全球所有國家編碼的映射關系,這時就派上用場了。
無論你以什么編碼存儲的數據,只要你的軟件在把數據從硬盤讀到內存里,轉成unicode來顯示,就可以了。
由於所有的系統、編程語言都默認支持unicode,那你的gbk軟件放到美國電腦上,加載到內存里,變成了unicode,
中文就可以正常展示啦。
Python3執行過程
1、解釋器找到代碼文件,把代碼字符串按文件頭定義的編碼加載到內存,轉成unicode
2、把代碼字符串按照語法規則進行解釋
3、所有的變量字符都會以unicode編碼聲明
在py3上 把你的代碼以utf-8編寫, 保存,然后在windows上執行。
發現可以正常執行!
其實utf-8編碼之所以能在windows gbk的終端下顯示正常,是因為到了內存里python解釋器把utf-8轉成了unicode ,
但是這只是python3, 並不是所有的編程語言在內存里默認編碼都是unicode,比如 萬惡的python2 就不是,
它是ASCII(龜叔當初設計Python時的一點缺陷),想寫中文,就必須聲明文件頭的coding為gbk or utf-8, 聲明之后,python2解釋器
僅以文件頭聲明的編碼去解釋你的代碼,加載到內存后,並不會主動幫你轉為unicode,也就是說,你的文件編碼是utf-8,
加載到內存里,你的變量字符串就也是utf-8, 這意味着什么?意味着,你以utf-8編碼的文件,
在windows是亂碼。
其實亂是正常的,不亂才不正常,因為只有2種情況 ,你的windows上顯示才不會亂。
Python2並不會自動的把文件編碼轉為unicode存在內存里。
1、字符串以GBK格式顯示
2、字符串是unicode編碼
所以我們只有手動轉,Python3 自動把文件編碼轉為unicode必定是調用了什么方法,這個方法就是,decode(解碼) 和encode(編碼)。
方法如下:
UTF-8/GBK --> decode 解碼 --> Unicode Unicode --> encode 編碼 --> GBK / UTF-8
例如:
#!/usr/bin/env python3 #-*- coding:utf-8 -*- # write by congcong
s = '匆匆'
print(s) s1 = s.decode("utf-8") # utf-8 轉成 Unicode,decode(解碼)需要注明當前編碼格式
print(s1,type(s1)) s2 = s1.encode("gbk") # unicode 轉成 gbk,encode(編碼)需要注明生成的編碼格式
print(s2,type(s2)) s3 = s1.encode("utf-8") # unicode 轉成 utf-8,encode(編碼)注明生成的編碼格式
print(s3,type(s3))
文件在 Python2 和 Python3 環境下運行結果的區別,如下所示:
#coding:utf-8
s = "你好,中國!"
print(s) # Python2輸出亂碼,Python3正常輸出
print(type(s)) # 均輸出 <type 'str'>
#解碼成unicode
s1 = s.decode("utf-8") print(s1) # Python2中輸出 “你好,中國!”,Python3顯示'str'對象沒有屬性'decode'
print(type(s1)) # Python2中輸出 <type 'unicode'> Python3中輸出 <class 'str'>
#編碼成gbk 或 utf-8
s2 = s1.encode('gbk') print(s2) # Python2中輸出 “你好,中國!”
print(type(s2)) # Python2中輸出 <type 'str'>
s3 = s1.encode('utf-8') print(s3) # Python2輸出亂碼,
print(type(s3)) # 輸出 <type 'str'>
編碼相互轉換的規則如下:
四、如何驗證編碼轉對了呢?
1、查看數據類型,python 2 里有專門的unicode 類型
2、查看unicode編碼映射表
unicode字符是有專門的unicode類型來判斷的,但是utf-8,gbk編碼的字符都是str,你如果分辨出來的當前的字符串數據是何種編碼的呢?
有人說可以通過字節長度判斷,因為utf-8一個中文占3字節,gbk一個占2字節。
看輸出的字節個數,也能大體判斷是什么類型。精確的驗證一個字符的編碼呢,就是拿這些16進制的數跟編碼表里去匹配。
關於 Unicode 與 GBK 等編碼對應關系(以中文“路”為例):

完整的編碼對應表可到這個網站下載:unicode與gbk的映射表 http://www.unicode.org/charts/

五、Python bytes類型
把8個二進制一組稱為一個byte,用16進制來表示。為的就是讓人們看起來更可讀。我們稱之為bytes類型,即字節類型。
python2的字符串其實更應該稱為字節串。 通過存儲方式就能看出來, 但python2里還有一個類型是bytes呀,難道又叫bytes又叫字符串?
嗯 ,是的,在python2里,bytes == str , 其實就是一回事。
除此之外呢, python2里還有個單獨的類型是unicode , 把字符串解碼后,就會變成unicode。
>>> s '\xe8\xb7\xaf\xe9\xa3\x9e' #utf-8
>>> s.decode('utf-8') u'\u8def\u98de' #unicode 在unicode編碼表里對應的位置
>>> print(s.decode('utf-8')) 路飛 #unicode 格式的字符
Python2的默認編碼是ASCII碼,當后來大家對支持漢字、日文、法語等語言的呼聲越來越高時,
Python於是准備引入unicode,但若直接把
默認編碼改成unicode的話是不現實的,
因為很多軟件就是基於之前的默認編碼ASCII開發的,編碼一換,那些軟件的編碼就都亂了。所以Python 2
就直接搞了一個新的字符類型,就叫unicode類型,比如你想讓你的中文在全球所有電腦上正常顯示,在內存里就得把字符串存成unicode類型。
>>> s = "路飛"
>>> s '\xe8\xb7\xaf\xe9\xa3\x9e'
>>> s2 = s.decode("utf-8") >>> s2 u'\u8def\u98de'
>>> type(s2) <type 'unicode'>
注意:
Python3 除了把字符串的編碼改成了unicode, 還把str 和bytes 做了明確區分, str 就是unicode格式的字符, bytes就是單純二進制啦。
在py3里看字符,必須得是unicode編碼,其它編碼一律按bytes格式展示。
Python只要出現各種編碼問題,無非是哪里的編碼設置出錯了
常見編碼錯誤的原因有以下這些:
- Python解釋器的默認編碼
- Python源文件文件編碼
- Terminal使用的編碼
- 操作系統的語言設置
最后總結一下:
python3 文件默認編碼是utf-8 , 字符串編碼是 unicode 以utf-8 或者 gbk等編碼的代碼,加載到內存,會自動轉為unicode正常顯示。 python2 文件默認編碼是ascii , 字符串編碼也是 ascii , 如果文件頭聲明了是gbk,那字符串編碼就是gbk。 以utf-8 或者 gbk等編碼的代碼,加載到內存,並不會轉為unicode,編碼仍然是utf-8或者gbk等編碼。