1. 程式人生 > >python中字元編碼:coding utf-8, unicde, defaultencoding, UnicodeDecodeError, UnicodeEncodeError

python中字元編碼:coding utf-8, unicde, defaultencoding, UnicodeDecodeError, UnicodeEncodeError

2.位元組與字元 計算機儲存的任何資料,包括各種文字、圖片、音視訊檔案等等,實際上都是一串二進位制數字01位元組序列組成的。相信大家都知道,一個位元組Byte(B)是8個位元bit(b)。 而字元,自然就是符號了。比如說二十六個英文字母,阿拉伯數字,以及在python中最坑爹的漢字都是字元。python中遇到的字元編碼問題,大部分都與漢字有關係。 寫過java的小夥伴都知道,java中的IO模組,從大的方向來說,就可以分為位元組流與字元流。位元組流包括InputStream與OutputStream,字元流包括Writer與Reader。 有的同學會有疑問,為什麼要搞這麼複雜?統一用位元組或者字元不就行了? 位元組一般用來儲存與網路傳輸,這樣可以節省儲存空間與網路傳輸頻寬。而字元主要是用於顯示,方便大家閱讀。試想你正在debug,結果所有的輸出是一堆01011100這種,那你還不得瘋了。

3.編碼(encoding)與解碼(decoding) 字元編碼(Character encoding)、字集碼是把字符集中的字元編碼為指定集合中某一物件(例如:位元模式、自然數序列、8位組或者電脈衝),以便文字在計算機中儲存和通過通訊網路的傳遞。常見的例子包括將拉丁字母表編碼成摩斯電碼和ASCII。其中,ASCII將字母、數字和其它符號編號,並用7位元的二進位制來表示這個整數。通常會額外使用一個擴充的位元,以便於以1個位元組的方式儲存。(參考文獻1) encoding是將字元轉換為位元組,那麼反過來將位元組轉換為字元則是decoding,兩者是可逆的。編碼主要是為了儲存傳輸,而解碼是為了方便閱讀。

4.utf-8與unicode區別 在正式講python字元編碼問題之前,還需要先扯清除unicode跟utf-8的關係。 簡單來說,unicode是一個字符集,而utf-8是一個編碼規則,兩者並不是同一維度的東西。 字符集:為每一個字元分配一個唯一的 ID(學名為碼位 / 碼點 / Code Point) 編碼規則:將碼位轉換為位元組序列的規則(編碼/解碼 可以理解為 加密/解密 的過程)

4.python 2.7中的字串 python2x中的字串實際有兩種型別: str與unicode。很多時候出現的各種問題,就是出現在這上面。 下面我們在python直譯器中簡單測試一下

s = “你好” s ‘\xe4\xbd\xa0\xe5\xa5\xbd’

type(s) <type ‘str’> 1 2 3 4 5 上面的程式碼可以看出,s是str型別,在內部的儲存方式就是一堆01二進位制位元組序列,顯示出來是一串十六進位制字元。 很多時候我們看到定義字串的時候會在前面加上一個字首u,這個u其實就是表示這個字串是unicode形式:

s = u"你好" s u’\u4f60\u597d’

type(s) <type ‘unicode’> 1 2 3 4 5 我們看看編碼的過程,大家記住編碼是從字元->位元組

s = u"你好" s u’\u4f60\u597d’

type(s) <type ‘unicode’>

s.encode(‘utf-8’) ‘\xe4\xbd\xa0\xe5\xa5\xbd’ 1 2 3 4 5 6 7 再看看解碼過程,解碼自然就是從位元組-> 字元

s = “你好” s ‘\xe4\xbd\xa0\xe5\xa5\xbd’

type(s) <type ‘str’>

s.decode(“utf-8”) u’\u4f60\u597d’ 1 2 3 4 5 6 7 5.UnicodeEncodeError 既然是UnicodeEncodeError,那麼應該是在字元->位元組的環節出現了問題。來看下面的例子。

def t1(): f = open(“ttt”, “w”) u1 = u’你好’ f.writelines(u1 + “\n”)

t1() 1 2 3 4 5 6 執行這段程式碼以後,會有如下問題:

TypeError: writelines() argument must be a sequence of strings 1 這個問題比較好解釋,writelines方法需要的是一個字串序列,而u1是個unicode。 將程式碼稍作修改

def t1(): f = open(“ttt”, “w”) u1 = u’你好’ f.write(u1)

t1() 1 2 3 4 5 6 UnicodeEncodeError: ‘ascii’ codec can’t encode characters in position 0-1: ordinal not in range(128) 1 呼叫write方法時,如果傳入的是字串,就直接將該str寫入檔案,無需編碼,因為str本身就是一堆二進位制的01位元組序列。 如果是unicode,那需要先用encode方法將unicode字串轉換為二進位制形式的str,才能儲存。 重點來了,剛剛我們有提到,unicode -> str是encode過程。既然是encode,肯定需要指定encode的方法,比如最常用的utf-8。坑爹就在於,python2中如果不指定encode的形式,預設是用ASCII碼來進行編碼。 很明顯,ASCII只有128個拉丁字母,是木有處理中文字元能力的。所以報錯裡面的資訊就是ordinal not in range(128)

解決方法很簡單,將encode方式指定為utf-8即可。

def t1(): f = open(“ttt”, “w”) u1 = u’你好’.encode(“utf-8”) f.write(u1)

t1() 1 2 3 4 5 6 6.UnicodeDecodeError 與UnicodeEncodeError對應的,UnicodeDecodeError肯定就是出現在位元組->字元的環節。

def t2(): u1 = u"啦啦啦" print repr(u1) byte1 = u1.encode(“utf-8”) print repr(byte1) byte1.decode(“gbk”)

t2() 1 2 3 4 5 6 7 8 u’\u5566\u5566\u5566’ ‘\xe5\x95\xa6\xe5\x95\xa6\xe5\x95\xa6’ … UnicodeDecodeError: ‘gbk’ codec can’t decode byte 0xa6 in position 8: incomplete multibyte sequence 1 2 3 4 把一個經過 UTF-8編碼後生成的位元組序列 '\xe5\x95\xa6\xe5\x95\xa6\xe5\x95\xa6’用GBK解碼成unicode的時候,因為GBK編碼只有兩位元組,而UTF-8是三位元組,多出來一個位元組,肯定無法解析。因此,要防止出現UnicodeDecodeError,主要就是保持編碼與解碼的時候所用的編碼方式一致。

7.coding:utf-8 python程式碼在開頭位置,一般都有這麼一行:

-*- coding: utf-8

1 作用是定義原始碼的編碼. 如果沒有定義, 此原始碼中是不可以包含中文字串的.

8.setdefaultencoding 在原始碼中經常還可以看到如下程式碼:

import sys reload(sys) sys.setdefaultencoding(‘utf8’) 1 2 3 上面幾行程式碼的作用是設定預設的string的編碼格式為utf8,在2.7以後已經不推薦使用這種方式了