完全弄懂python編碼

  在編寫python程序的過程當中,中英文混用常常會出現編碼問題。圍繞此問題,本文首先介紹編碼的含義及經常使用編碼,隨後列舉幾個python常常遇到的編碼異常及解決方法,接着列舉筆者在實踐中遇到的異常出現的情景及緣由,最後針對編碼問題提出最佳實踐。html

一 常見編碼

1.1 unicode編碼

  在文本文件中,看到的全部字符,包括中文,都須要在計算機中存儲,而計算機只能存儲0和1這樣的二進制位,因此須要一種方法,將字符映射成數字,而後將數字轉化爲二進制位存儲在計算機中。針對字符和數字的映射的問題,產生了unicode編碼,unicode將世界上的全部字符映射爲惟一的數字。unicode數字並非直接就能夠轉化爲二進制存儲,好比假設中文字符‘中’映射爲數字1(00000001),‘國’映射爲數字2(00000010),因爲漢字不少,單字節並不能表示完全部的漢字,故可能會有漢字的unicode數字爲258(00000001 00000010),假設爲‘京’,如今在字符串中碰到存儲爲00000001 00000010的二進制串,不能區分出其實際表明的是「中國」仍是「京」。python

  針對unicode數字和二進制的映射問題,有兩種解決方法:一種是每一個unicode數字用固定寬度的二進制位表示,好比都用兩字節,由此產生了ASCII、GB23十二、GBK編碼;另外一種是存儲的二進制位除了表示數字以外,還表示每一個unicode數字的長度,由此產生了utf-8編碼。編程

1.2 ASCII編碼

ASCII編碼用單字節表示字符,最高位固定爲0,故最多隻能表示128個字符,當編程只涉及到英文字符或數字時,不涉及中文字符時,可使用ASCII編碼。工具

1.3 GB2312編碼、GBK

  GB(GuoBiao)爲國標,GBK(GuoBiao Kuozhan)表示國標擴展。GB2312兼容ASCII編碼,對於ASCII能夠表示的字符,如英文字符‘A’、‘B’等,在GB2312中的編碼和ASCII編碼一致,佔一個字節,對於ASCII不能表示的字符,GB2312用兩個字節表示,且最高位不爲0,以防和ASCII字符衝突。例如:‘A’在GB2312中存儲的字節十六進制爲41,在ASCII中也是41,中文字符‘中’在GB2312中存儲的兩個字節十六進制爲D6D0,最高位爲1不爲0。編碼

  GB2312只有6763個漢字,而漢字特別多。GBK屬於GB2312的擴展,增長了不少漢字,同時兼容GB2312,一樣用兩個字節表示非ASCII字符。spa

1.4 UTF-8編碼

  和GB系列不一樣,UTF-8能夠將全世界全部的unicode數字表示出來。UTF-8兼容ASCII編碼,不兼容GB系列編碼,所以,若文本中UTF-8和GB系列編碼混用,會出現亂碼問題。UTF-8對於每一個字符的存儲,用最高二進制位開始連續1的個數表示字的長度,最高位爲0表示單字節,用來兼容ASCII字符,爲110表示雙字節,非字符首字節的字節都以10開始,以下表格所示。例如:字符‘中’的unicode編碼爲2D4E(00101101 01001110),用UTF-8存儲的二進制爲E4B8AD(11100100 10111000 10101101 ),存儲在計算機中的首字節爲1110開頭,表示此字符佔三個字節,去掉開始字節表示長度的1110和其他字節開頭的10,能夠獲得01001110 00101101(4E2D),能夠看到和unicode數字恰好相反,是由於是大端存儲方式,高字節存儲在內存中的低地址端,反過來即爲unicode編碼。操作系統

 

字節數 二進制編碼格式
單字節 0XXXXXXX
雙字節 110XXXXX 10XXXXXX
三字節 1110XXXX 10XXXXXX 10XXXXXX
四字節 11110XXX 10XXXXXX 10XXXXXX 10XXXXXX
五字節 111110XX 10XXXXXX 10XXXXXX 10XXXXXX 10XXXXXX
六字節 1111110X 10XXXXXX 10XXXXXX 10XXXXXX 10XXXXXX 10XXXXXX

二 python字符序列及編碼問題

  上一節對幾種常見的編碼原理作出了介紹,以便理解python因爲編碼引發的異常,本節將對python中的字符串做出介紹,並在此基礎上提出幾種常見的編碼異常,並提供解決方案。.net

2.1 python2和python3字符序列

   python2中字符序列有兩種類型:unicode和str。unicode字符序列存儲的元素爲unicode字符。如圖2.1所示,unicode_string表明unicode字符序列「中國」,其長度爲2,剛好表示兩個unicode字符。code

   圖2.1 unicode字符序列htm

  python2中的另外一種字符序列是str類型,str類型的字符序列實際上是unicode字符序列encode以後的值,用不一樣的編碼類型encode,得出的值不同。str字符序列的元素爲字節,如圖2.2所示,「中國」 的str字符序列長度爲6,爲UTF-8編碼後所佔字節長度。

圖2.2 str字符序列

  與unicode字符串轉化爲str類型用encode相反,str類型的字符序列轉化爲unicode字符串,能夠經過decode方法,如圖2.3所示:

圖2.3 str轉化爲unicode

   python3中的字符序列也有兩種類型:bytes和str。python3中的bytes和python2中的str類似,str和python2中的unicode類似。這裏要注意,str類型在python3和python2中都有,但含義徹底變了。

圖2.4 python3的str和bytes字符序列

2.2常見編碼問題

2.2.1 UnicodeEncoderError

  將文本轉化爲字節序列時,如有字符在目標編碼中沒有定義,則會出現UnicodeEncoderError。如圖2.5所示,因爲中文字符在ascii編碼中無定義,則會報出編碼錯誤。對於此類問題,需選擇合適的編碼類型,好比含有中文字符,通常用UTF-8編碼類型對unicode字符串編碼。

圖2.5 UnicodeEncodeError示例

 

2.2.2 UnicodeDecodeError

  把二進制序列轉化爲文本時,遇到沒法轉換的字節序列,則會發生此異常。好比用UTF-8編碼後的二進制序列,用GB2312解碼,因爲兩種編碼不兼容,用GB2312不能識別字節序列,則會出現異常,如圖2.6所示。

圖2.6 UnicodeDecodeError示例

  碰到這種異常,是因爲decode使用的編碼和字節序列的編碼不一致,能夠用字符編碼偵測包chardet檢測字節序列的編碼,而後再用此編碼解碼。如圖2.7所示:

 

圖2.7 編碼檢測

三 實踐中常見編碼異常場景

3.1 字符串鏈接

python代碼

1 # -*- coding: utf-8 -*-
2 unicode_string=u'中國'
3 str_string='中國'
4 merge_string= str_string+unicode_string #UnicodeDecodeError: 'ascii' codec can't decode byte 0xe4 in position 0: ordinal not in range(128)

python代碼

1 # -*- coding: utf-8 -*-
2 unicode_string=u'中國'
3 str_string='中國'
4 "中國:%s" % str_string
5 #兩種字符序列混用,至關於"中國:%s".decode('ascii')%unicode_string
6 "中國:%s" % unicode_string #UnicodeDecodeError: 'ascii' codec can't decode byte 0xe4 in position 0: ordinal not in range(128)
7 u"中國:%s"%unicode_string
8 #兩種字符序列混用,至關於u"中國:%s"%str_string.decode('ascii')
9 u"中國:%s"%str_string #UnicodeDecodeError: 'ascii' codec can't decode byte 0xe4 in position 0: ordinal not in range(128)

  當str類型字符串和unicode類型字符串混合運算時,python默認會將str類型字符串轉化爲unicode字符串,因爲不知道str類型字符串的編碼格式,會使用 sys.getdefaultencoding() ,而默認的defaultencoding通常是ascii,故會出錯。

3.2 print中文問題

 如圖3.1,python打印變量時,操做系統會對變量進行相應的處理,若變量是str類型,則操做系統直接發送到終端顯示,若變量是unicode類型,則操做系統會對變量用sys.stdout.encoding編碼對變量encode,若變量中含有sys.stdout.encoding未定義字符,則會出現UnicodeEncodeError。編碼後字節序列被髮送給終端,倘若終端設置的編碼和str編碼不一致,終端就會顯示出亂碼。

圖3.1 print過程

四 最佳實踐

  編寫python程序時,爲避免不一樣類型字符串混用出現編解碼異常,要把編碼和解碼操做放在程序的最外圍來作,程序的核心邏輯統一使用unicode字符類型。下面分別對python2和python3編寫了外圍編碼轉換工具類。

  

 1 #python2,unicode和utf-8類型的str互相轉換
 2 #file:python2_endecode_helper.py
 3 
 4 # -*- coding: utf-8 -*-
 5 def to_unicode(unicode_or_str):
 6     if isinstance(unicode_or_str, str):
 7         value = unicode_or_str.decode('UTF-8')
 8     else:
 9         value = unicode_or_str
10     return value
11 
12 def to_str(unicode_or_str):
13     if isinstance(unicode_or_str, unicode):
14         value = unicode_or_str.encode('UTF-8')
15     else:
16         value = unicode_or_str
17     return value
18 
19 if __name__=='__main__':
20     unicode_string = u'中國'
21     value = to_str(unicode_string)
22     print type(value) #<type 'str'>
23     value = to_unicode(value)
24     print type(value) #<type 'unicode'>

 

#python3,str和bytes類型相互轉換工具類
#file:python3_endecode_helper.py
def to_str(bytes_or_str):
    if isinstance(bytes_or_str,bytes):
        value = bytes_or_str.decode('UTF-8')
    else:
        value = bytes_or_str
    return value

def to_bytes(bytes_or_str):
    if isinstance(bytes_or_str,str):
        value = bytes_or_str.encode('UTF-8')
    else:
        value = bytes_or_str
    return value

if __name__=='__main__':
    str_string = u'中國'
    value = to_bytes(str_string)
    print(type(value)) #<class 'bytes'>
    value = to_str(value)
    print(type(value)) #<class 'str'>

 

參考文獻 

[1] Brett Slatkin. Effective Python[M]. 北京: 機械工業出版社, 2016: 5-7[2] Luciano Ramalho. Fluent Python[M]. 北京: 人民郵電出版社, 2017: 89- 91[3] Jinhaolin. python2編碼總結. https://www.cnblogs.com/jinhaolin/p/5128973.html[4] In355hz. 也談 Python 的中文編碼處理. http://in355hz.iteye.com/blog/1860787[5] 董公子. python中文編碼問題:print打印中文異常及顯示亂碼問題分析與解決. https://blog.csdn.net/qq_26580757/article/details/79922043

相關文章
相關標籤/搜索