1. 程式人生 > >python學習之unicode編碼

python學習之unicode編碼

python內建的字串有兩種型別:strUnicode,它們擁有共同的祖先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與位元組碼

  1. s ="中國北京"

s是個字串,它本身儲存的就是位元組碼。那麼這個位元組碼是什麼格式的?

如果這段程式碼是在直譯器上輸入的,那麼這個s的格式就是直譯器的編碼格式,對於windows的cmd而言,就是gbk。

如果將段程式碼是儲存後才執行的,比如儲存為utf-8,那麼在直譯器載入這段程式的時候,就會將s初始化為utf-8編碼。

unicode與str

python 在內部使用兩個位元組來儲存一個unicode,使用unicode物件而不是str,好處就是unicode方便於跨平臺。

你可以用如下兩種方式定義一個unicode:

  1. s1 = u"中國北京"
  2. s2 = unicode
    ("中國北京","utf-8")

encode與decode

  • decode()方法講其他編碼對應的字串解碼為Unicode,
  • encode()方法將Unicode編碼轉換為另一種編碼。

我們可以寫這樣的程式碼:

  1. # -*- coding: utf-8 -*-
  2. # su是一個utf-8格式的位元組串
  3. su ="中國北京"
  4. # s被解碼為unicode物件,賦給u
  5. u = s.decode("utf-8")
  6. # u被編碼為gbk格式的位元組串,賦給sg
  7. sg = u.encode("gbk")
  8. print sg

看下面這個例子:

  1. s ="中國北京"
  2. 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碼,所以會出錯。

上面程式碼等價於:

  1. s ="中國北京"
  2. # sys.getdefaultencoding() == "ascii"
  3. s.decode(sys.getdefaultencoding()).encode('gbk')

解決方法如下:

  1. # 重新設定預設編碼
  2. reload(sys)
  3. sys.setdefaultencoding('utf-8')
  4. s ="中國北京"
  5. s.encode('gbk')

再比如你使用str建立unicode物件時,如果不說明這個str的編碼格式,那麼程式也會使用defaultencoding。

  1. # 兩者等價
  2. u = unicode("中國北京")
  3. u = unicode("中國北京", sys.getdefaultencoding())

預設的defaultcoding:ascii是許多錯誤的原因,所以早早的設定defaultencoding是一個好習慣。

普通字元和Unicode進行字串連線的時候丟擲UnicodeDecodeError異常。

  1. s ="中文北京"+ u"Chinese Test"
  2. print s

原因:使用 + 操作符來進行字串的連線時,左邊為中文字串,型別為str,右邊為Unicode字串,當兩種型別的字串連線的時候,python將左邊的中文字串轉換為Unicode再與右邊的Unicode字串連線,將str轉換為Unicode時使用系統預設的ASCII編碼對字串進行編碼,就會出現UnicodeDecodeError異常。

解決方法:

  1. 指定str轉換為Unicode時的編碼方式。
    1. s ="中文北京".decode('utf-8')+ u"Chinese Test"
  2. 將Unicode字串進行UTF-8編碼
    1. s ="中文北京"+ u"Chinese Test".encode("utf-8")

檔案頭宣告編碼

一般來說進行原始檔編碼宣告有三種方式:

  1. #coding=utf-8
  2. # 大多推薦使用此方式
  3. #!/usr/bin/python
  4. # -*- coding: utf-8 -*-
  5. #!/usr/bin/python
  6. # vim: set fileencoding=utf-8:

檔案頭宣告編碼的作用:

  • 如果程式碼中有中文註釋,就需要此宣告。
  • 比較高階的編輯器(比如我的emacs),會根據頭部宣告,將此作為程式碼檔案的格式。
  • 程式會通過頭部宣告,解碼初始化 u"中國北京"這樣的unicode物件,(所以頭部宣告和程式碼的儲存格式要一致)。

幾點建議

基本設定

  • 主動設定defaultencoding(預設的是ascii)
  • 程式碼檔案的儲存格式要與檔案頭部的# -*- coding:xxx -*- 一致
  • 如果是中文,程式內部儘量使用unicode,而不用str

關於列印
你在列印str的時候,實際就是直接將位元組流傳送給shell。如果你的位元組流編碼格式與shell的編碼格式不相同,就會亂碼。
而你在列印unicode的時候,系統自動將其編碼為shell的編碼格式,是不會出現亂碼的。

程式內外要統一
如果說程式內部要保證只用unicode,那麼在從外部讀如位元組流的時候,一定要將這些位元組流轉化為unicode,在後面的程式碼中去處理unicode,而不是str。

  1. with open("test.txt")as f:
  2. for i in f:
  3. # 將讀入的utf-8位元組流進行解碼
  4. u = i.decode('utf-8')
  5. ....

如果把連線程式內外的這段資料流比喻成通道的的話,那麼與其將通道開為位元組流,讀入後進行解碼,不如直接將通道開為unicode的。

  1. # 使用codecs直接開unicode通道
  2. file = codecs.open("test.txt","r","utf-8")
  3. for i in file:
  4. print type(i)
  5. # i的型別是unicode的

所以python處理中文編碼問題的關鍵是你要清晰的明白,自己在幹什麼,打算讀入什麼格式的編碼,宣告的的這些位元組是什麼格式的,str到unicode是如何轉換的,str的一種編碼到另一種編碼又是如何進行的。 還有,你不能把問題變得混亂,要自己主動去維護一種統一。