python學習之unicode編碼
python內建的字串有兩種型別:str
和Unicode
,它們擁有共同的祖先basestring。
Unicode也稱做萬國碼,它為每種語言設定了唯一的二進位制編碼表示方式,提供從數字程式碼到不同語言字符集之間的對映,從而可以滿足跨平臺、誇語言之間的文字處理要求。
Unicode編碼系統可以分為編碼方式和實現方式兩個層面。
- 在編碼方式上,分為UCS-2和UCS-4兩種方式,UCS-2用兩個位元組編碼,UCS-4用4個位元組編碼。
- 一個字元的Unicode編碼是確定的,但是在實際傳輸過程中,由於系統平臺的不同以及處於節省空間的目的,實現方式有所差異。Unicode的實現方式稱為Unicode轉換格式,簡稱為UTF,包括UTF-7、UTF-16、UTF-32、UTF-8等,較為常見的是UTF-8,他的特點是對不同範圍的字元使用不同長度的編碼,其中0x00 ~ 0x7F的字元的UTF-8編碼與ASCII編碼完全相同,UTF-8編碼的最大長度是4個位元組。
str與位元組碼
s ="中國北京"
s是個字串,它本身儲存的就是位元組碼。那麼這個位元組碼是什麼格式的?
如果這段程式碼是在直譯器上輸入的,那麼這個s的格式就是直譯器的編碼格式,對於windows的cmd而言,就是gbk。
如果將段程式碼是儲存後才執行的,比如儲存為utf-8,那麼在直譯器載入這段程式的時候,就會將s初始化為utf-8編碼。
unicode與str
python 在內部使用兩個位元組來儲存一個unicode,使用unicode物件而不是str,好處就是unicode方便於跨平臺。
你可以用如下兩種方式定義一個unicode:
s1 = u"中國北京"
s2 = unicode
encode與decode
decode()
方法講其他編碼對應的字串解碼為Unicode,encode()
方法將Unicode編碼轉換為另一種編碼。
我們可以寫這樣的程式碼:
# -*- coding: utf-8 -*-
# su是一個utf-8格式的位元組串
su ="中國北京"
# s被解碼為unicode物件,賦給u
u = s.decode("utf-8")
# u被編碼為gbk格式的位元組串,賦給sg
sg = u.encode("gbk")
print sg
看下面這個例子:
s ="中國北京"
s.encode('gbk')
上面的程式碼會報錯,錯誤資訊:UnicodeDecodeError:
'ascii' codec can't decode byte 0xe4 in position 0: ordinal not in range(128)
原因:當對str進行編碼時,會先用預設編碼將自己解碼為unicode,然後在將unicode編碼為你指定編碼,而python的預設編碼defaultencoding是ascii碼,所以會出錯。
上面程式碼等價於:
s ="中國北京"
# sys.getdefaultencoding() == "ascii"
s.decode(sys.getdefaultencoding()).encode('gbk')
解決方法如下:
# 重新設定預設編碼
reload(sys)
sys.setdefaultencoding('utf-8')
s ="中國北京"
s.encode('gbk')
再比如你使用str建立unicode物件時,如果不說明這個str的編碼格式,那麼程式也會使用defaultencoding。
# 兩者等價
u = unicode("中國北京")
u = unicode("中國北京", sys.getdefaultencoding())
預設的defaultcoding:ascii是許多錯誤的原因,所以早早的設定defaultencoding是一個好習慣。
普通字元和Unicode進行字串連線的時候丟擲UnicodeDecodeError
異常。
s ="中文北京"+ u"Chinese Test"
print s
原因:使用 + 操作符來進行字串的連線時,左邊為中文字串,型別為str,右邊為Unicode字串,當兩種型別的字串連線的時候,python將左邊的中文字串轉換為Unicode再與右邊的Unicode字串連線,將str轉換為Unicode時使用系統預設的ASCII編碼對字串進行編碼,就會出現
UnicodeDecodeError
異常。
解決方法:
- 指定str轉換為Unicode時的編碼方式。
s ="中文北京".decode('utf-8')+ u"Chinese Test"
- 將Unicode字串進行UTF-8編碼
s ="中文北京"+ u"Chinese Test".encode("utf-8")
檔案頭宣告編碼
一般來說進行原始檔編碼宣告有三種方式:
#coding=utf-8
# 大多推薦使用此方式
#!/usr/bin/python
# -*- coding: utf-8 -*-
#!/usr/bin/python
# vim: set fileencoding=utf-8:
檔案頭宣告編碼的作用:
- 如果程式碼中有中文註釋,就需要此宣告。
- 比較高階的編輯器(比如我的emacs),會根據頭部宣告,將此作為程式碼檔案的格式。
- 程式會通過頭部宣告,解碼初始化
u"中國北京"
這樣的unicode物件,(所以頭部宣告和程式碼的儲存格式要一致)。
幾點建議
基本設定
- 主動設定defaultencoding(預設的是ascii)
- 程式碼檔案的儲存格式要與檔案頭部的# -*- coding:xxx -*- 一致
- 如果是中文,程式內部儘量使用unicode,而不用str
關於列印
你在列印str的時候,實際就是直接將位元組流傳送給shell。如果你的位元組流編碼格式與shell的編碼格式不相同,就會亂碼。
而你在列印unicode的時候,系統自動將其編碼為shell的編碼格式,是不會出現亂碼的。
程式內外要統一
如果說程式內部要保證只用unicode,那麼在從外部讀如位元組流的時候,一定要將這些位元組流轉化為unicode,在後面的程式碼中去處理unicode,而不是str。
with open("test.txt")as f:
for i in f:
# 將讀入的utf-8位元組流進行解碼
u = i.decode('utf-8')
....
如果把連線程式內外的這段資料流比喻成通道的的話,那麼與其將通道開為位元組流,讀入後進行解碼,不如直接將通道開為unicode的。
# 使用codecs直接開unicode通道
file = codecs.open("test.txt","r","utf-8")
for i in file:
print type(i)
# i的型別是unicode的
所以python處理中文編碼問題的關鍵是你要清晰的明白,自己在幹什麼,打算讀入什麼格式的編碼,宣告的的這些位元組是什麼格式的,str到unicode是如何轉換的,str的一種編碼到另一種編碼又是如何進行的。 還有,你不能把問題變得混亂,要自己主動去維護一種統一。