解決Python2.x編碼之殤

  Python編碼問題一直困擾了我許久,之前有過一些總結,但並不系統,比較凌亂。當然python2.x編碼問題本身,便是剪不斷理還亂。本篇將系統介紹python2.x編程中會遇到的一些編碼問題,並給出解決方案。基於對編碼問題的摸索了解,我也嘗試寫了一個編碼轉換模塊Transcode,應該能解決絕大部分新手的疑難雜症。當然,python大神可以繞道而行,至於使用3.x的朋友,以後將會成文介紹。

  python編程中會經常遇到操作系統編碼、文件編碼、控制台輸入輸出編碼、網頁編碼、源代碼編碼、python編碼,本文將會逐一介紹。首先我們來看看一些常見的編碼情況:

print sys.getdefaultencoding() #系統默認編碼nprint sys.getfilesystemencoding() #文件系統編碼nprint locale.getdefaultlocale() #系統當前編碼nprint sys.stdin.encoding #終端輸入編碼nprint sys.stdout.encoding #終端輸出編碼n

將以上這段代碼在windows與linux系統下分別運行,查看輸出結果。

windows終端結果:

asciinmbcsn(zh_CN, cp936)ncp936ncp936n

Linux終端結果:

asciinUTF-8n(zh_CN, UTF-8)nUTF-8nUTF-8n

操作系統編碼

  操作系統默認編碼可以通過sys.getdefaultencoding()函數獲取,可以看到windows與linux下默認都為ascii編碼,而我們知道ascii編碼不支持中文。那麼操作系統編碼將在python程序的何處會被用到呢?何時又會引發血案?

觸發異常點

  經過測試,我發現當需要將unicode格式的字元串存入到文件時,python內部會默認將其先轉換為Str格式的系統編碼,然後再執行存入步驟。而在這過程中,容易引發ascii異常。

實例證明:

#! -*- coding:utf-8 -*-na=u"中文"nf=open("test.txt","w")nf.write(a)n

報錯異常信息:UnicodeEncodeError: 『ascii』 codec can』t encode characters in position 0-1……

說明:因為ascii不支持中文,而變數a為unicode格式的中文字元串,因此無法進行編碼而引發異常。

解決方案

設置系統編碼為utf-8或者gbk。

import sysnreload(sys)nsys.setdefaultencoding(gbk)n

說明:在windows下將其設置為gbk,在linux在設置為utf-8.

終端編碼

  windows下終端指的是控制台,在控制台上輸入輸出有著其本身的編碼格式,如windows控制台輸入輸出編碼都為cp936。原諒我是第一次看到此編碼,於是上網查了會,發現其實它就是常見的GBK編碼;而linux終端的輸入輸出編碼都為utf-8。如果我們編寫的程序,不會再終端輸入輸出任何內容,則可以忽略此編碼,如若不然終端編碼將會非常重要。

亂碼點

我們在終端執行python腳本時,經常會遇到輸出中文亂碼,而這往往是因為輸出的字元串本身編碼與控制台編碼不一致。

實例證明:

#! -*- coding:utf-8 -*-na="中文" #定義一個變數,默認為Str,utf-8編碼nprint anprint type(a)n

windows控制台輸出結果:

浣犲ソn<type str>n

linux終端輸出結果:

中文n<type str>n

造成這種差異的原因在於windows控制台為gbk編碼,而變數a本身為utf-8編碼。

解決方案

#! -*- coding:utf-8 -*-na=你好nb=a.decode("utf-8").encode("gbk")nprint bn

將變數a從utf-8編碼轉換為gbk編碼。

python編碼

  python2.x從外部獲取的內容都是string編碼,其內部分為String編碼與Unicode編碼,而String編碼又分為UTF-8,GBK,GB2312等等。因此為了避免不同編碼造成的報錯,python內部最好都轉化為unicode編碼,在輸出時再轉化為str編碼 。可以用encode()/decode()函數,將string與unicode編碼互換。

觸發異常點

基本在於python內部變數編碼與控制台編碼,或者其他編碼相結合時觸發。

實例證明:

#! -*- coding:utf-8 -*-na="中文" #定義一個變數,默認為str,utf-8編碼nprint anprint type(a)n

運行結果:

浣犲ソn<type str>n

  說明:windows下控制台輸入輸出都是gbk編碼格式,而代碼中定義的變數a為str,utf-8格式,所以會出現亂碼。如果想創建一個unicode編碼字元串的變數,則可以a=u」123」,在雙引號前面加上一個u,表示a為unicode編碼。

解決方案

#! -*- coding:utf-8 -*-na=你好nprint a.decode("utf-8").encode("gbk")n

  說明:首先我們定義的變數a是str格式,編碼為utf-8的字元串,我們要將之轉化為str格式,GBK編碼的字元串。在python內部無法直接轉化,需要藉助decod()與encode()函數。decode()函數先將str格式的字元串a轉化為unicode,再將unicode編碼為str格式GBK。而在Unix系統下,不存在這個問題,因為都是utf-8編碼,不會存在亂碼。print語句默認會將unicode編碼的字元串,encode為相應系統的str編碼並輸出(windows下為gbk,unix下為utf-8),因此不用擔心print unicode編碼字元串會報錯。

源代碼編碼

源代碼編碼指的是python程序本身的編碼,默認為ascii。

觸發異常點

  python程序本身要被解釋器解析執行,需要先被轉化為二進位代碼。而在這過程中容易引發異常,原因同樣是ascii不支持中文,因此當python程序中出現中文時,哪怕是注釋,也會引發ascii異常。

實例證明:

print "中文" #中文注釋n

報錯:SyntaxError: Non-ASCII character 『xe7』……

解決方案

#! -*- coding:utf-8 -*-n

python程序開頭加上這句代碼,指定python源代碼編碼格式為utf-8。

文件編碼

  文件編碼指的是,python程序從文件中獲取的內容的編碼格式。可以用sys.getfilesystemencoding()函數獲取,windows下為mbcs,linux下為utf-8。至於mbcs,是一種多位元組編碼(沒搞很明白)。

觸發異常點(讀取文件內容)

當python程序從文件中獲取內容,並輸出時,容易觸發異常。

實例證明:

#! -*- coding:utf-8 -*-nf=open("test.txt","r")ncontent=f.read()nprint type(content)nprint contentn

運行結果:

<type str>n你好n

  可以看到windows下,從文件中讀取的編碼格式為Str,GBK格式(因為控制台輸出沒有中文亂碼);而在Unix下為Str,Utf-8格式。從輸出內容來說,並沒有觸發異常,然而當這些內容與python程序自身內容相結合時,容易觸發異常。

解決方案

在windows下,最好將文件內容轉為unicode,可以使用codecs:

f=codecs.open("test.txt", encoding=gbk).read()n

將格式為gbk的文件內容轉化為unicode格式,當然也可以直接使用open(「」,」r」).read().decode(「gbk」)

觸發異常點(寫入文件內容)

參考操作系統編碼觸發異常點,即將中文unicode字元寫入文件時,容易觸發異常。

解決方案

參考操作系統編碼解決方案,或者手動將unicode編碼轉換為str編碼。

實例證明:

#! -*- coding:utf-8 -*-na=u"中文" #a為unicode格式編碼nf=open("test.txt","w")nf.write(a.encode("gbk"))n

當然如果變數a本身就是Str則不會報錯,只是utf-8編碼的內容寫入windows文件中,顯示會亂碼。

網頁編碼

  網頁編碼,通常在寫爬蟲的時候經常遇到,再結合系統編碼,python編碼,文件編碼,往往會搞得一團亂。在程序中我們應該分別處理這些編碼,在python內部全部轉化為unicode。那麼網頁編碼又有哪些格式呢?

常見格式:utf-8,gbk,gb2312

觸發異常點

還是在於從網頁中獲取的源碼編碼與終端編碼,甚至python內部編碼不一致的情況。

實例證明:

#!coding=utf-8nimport urllib2nbody=urllib2.urlopen(http://thief.one).read()nprint type(body)nprint bodyn

運行結果:

<type str>nbody中文顯示亂碼n

說明:這個網站的編碼是utf-8,而且python從網頁上爬取的內容都為Str格式,在windows控制台下輸出會亂碼。

解決方案

  依照之前做法,先將其轉化為unicode。而相應的正則也可以為unicode編碼,如:res=r』』+u」新成員」。可以通過chardet模塊判斷網頁編碼類型,返回的是一個帶概率的字典。

編碼判斷

判斷字元串編碼

isinstance(obj, (str, unicode))n

返回True或者False

判斷網頁編碼

import chardetnimport urllib2nbody=urllib2.urlopen("http://thief.one").read()nchardet.detect(body)n

判斷編碼格式,會有百分比,一般用來判斷網頁編碼比較好。

判斷系統編碼

print sys.getdefaultencoding() #系統默認編碼nprint sys.getfilesystemencoding() #文件系統編碼nprint locale.getdefaultlocale() #系統當前編碼nprint sys.stdin.encoding #終端輸入編碼nprint sys.stdout.encoding #終端輸出編碼n

python2.x編碼建議

  • 請盡量在Linux系統上編程,綜上我們可以知道linux下較windows,編碼問題良好很多。
  • python代碼內部請全部使用unicode編碼,在獲取外部內容時,先decode為unicode,向外輸出時再encode為Str
  • 在定義變數或者正則時,也定義unicode字元,如a=u」中文」;res=r」」+u」正則」。

其他疑難雜症

實例一:

a="違法違規"nprint an

變數a的內容本身為unicode編碼,怎麼正常顯示輸入?

解決方案:

a="違法違規" # unicode轉化為中文nb=a.decode(unicode-escape)nprint bn

  如果閱讀完本章,增加了您對python編碼問題的認識,那我會感到欣慰,如有python編碼上的問題可以在下方留言。

  如果閱讀完本章,您仍然不知如何解決python亂碼問題,沒關係,請繼續移步閱讀Transcode解決python編碼問題

  為了能夠讓您重視,我不得不再次重申:解決python2.x編碼問題的關鍵,在於要明白無論從哪裡來的內容,在python內部流通時,都應該先轉換為unicode。(python3.x在這方面做了改進,並取得了很好的效果)

技術的探索,就好像編織故事一般,其樂趣在於偶爾能夠講述給別人聽,並獲得一些贊同!

原文地址:解決Python2.x編碼之殤


推薦閱讀:

字元編碼總結
編碼如作文:寫出高可讀 JS 的 7 條原則
輸入URL以後和編碼
計算機最底層的機器語言是如何變成物理電平信號輸給CPU的呢?
Ingress 中的 passcode 有哪些解碼技巧?

TAG:Python | 编码 | 信息安全 |