本人工作使用Python2.7,在業余做玩具的時候使用的Python3.5,今天在使用zipfile模塊的時候,遇到zip文件中有中文的文件夾,導致亂碼。所以在解決問題的時候,順道就摸索了一下編碼問題的差異。
如有錯誤,請指正!
一般我們遇到的編碼問題,主要是兩類:文件的編碼和數據的編碼。
- 文件的編碼問題,請再編輯器中設置文件的編碼為utf-8,如果使用中文注釋,Python2和3的處理方式相同,請在第一行或第二行加一個編碼申明即可('# -- coding=utf-8 --' 或者 '# encoding=utf-8')
- 而數據的編碼,Python2與3稍微有些差別。這里也主要記錄下數據編碼的問題。
是什么導致的編碼問題
在語言設計之初,都是使用英文,所以字符集是ascii碼。即在存儲的時候,使用只占一個字節的數字對映射一個字符,你想呀,一個字節可以表示的最大字符數是255(00H—FFH)。這對于英文而言,是完全足夠的,一般只用到前128個(00H--7FH,最高位為0)。而最高位為1 的另128 個字符(80H—FFH)被稱為“擴展ASCII”,一般用來存放英文的制表符、部分音標字符等等的一些其它符號。
后來國際化了,到中國,發現ASCII碼不夠用呀。咋辦?
于是中國的編碼方式出現了:GB2312。它是和ASCII 兼容的一種編碼規范, 其實就是利用擴展ASCII沒有真正標準化這一點,把一個中文字符用兩個擴展ASCII 字符來表示,以區分ASCII 碼部分。但是中文的文字編碼和擴展ASCII 碼有重疊呀:把兩個英文符號解釋為中文的一個字或者把一個中文字解釋為兩個英文符號,就“亂碼”了。同樣的問題再其他國家也類似。
為了解決這個問題,unicode字符集就出現了。并且再后面的發展中把全世界所有的文字都考慮進來,給每一個文字分配一個編碼。因為編碼方式的不同,所以可以使用2個字節來表示,也可使用4個字節來表示。
所以編碼問題主要原因是不同字符集以及其編碼方式不同導致的。
Python2與3在編碼的差異
- 文件編碼:都是使用utf-8,此處是針對文件BOM頭(文件最前頭的不可見的內容,幫助編輯器識別文件編碼方式的一個標識)。
- Python環境編碼方式:都需要文件第一行或者第二行申明文件編碼方式。
- Python變量編碼方式:
在Python2中主要有str和unicode兩種字符串類型,而到Python3中改為了bytes和str。因為Python3默認了字符串類型為unicode(所以再3中,字符串拋棄了u'中文'的定義方式,但Python3中如果是寫或者讀bytes類型就必需帶上'b')。
- | 字符串類型 | 默認編碼 |
---|---|---|
Python2 | str & unicode | 默認采取的ASCII編碼,字母、標點和其他字符只使用一個字節來表示,但對于中文字符來說,一個字節滿足不了需求。使用u'中文'方式申明變量。 |
Python3 | bytes & str | 默認用unicode編碼,所以申明變量不用使用u'中文'方式。 |
根據上表可以看出來,代碼中申明的變量的問題就解決了。
但是外來的數據呢?
比如說讀取文件中的數據或者web前端傳過來的數據呢?
其實很簡單,而且Python2和3的處理方式是有一些差異的。
就拿我在使用ziplib,舉個例子:比如ZipFile對象中的info_list的filename的編碼方式為cp437。
Python3中,那么filename為unicode的str。那么首先需要把filename翻譯為計算機認識的字符串:filename.encode('cp437'),然后再轉為人能看到的字符串:decode('gbk')。
此處可能有同學要問了,為什么decode不使用utf-8方式呢?
因為cp437和gbk是ASCII編碼,而utf-8為unicode編碼了,在解碼過程中decode為utf-8,直接不認識呀,那么就出錯了。Python2中,那么filename為ascii碼的str。那么想象下要輸出中文,首先他要是一個unicode,那毫不猶豫要是unicode(filename, 'utf-8')或者unicode(filename, 'gbk')就可以。