前言
本文首先對Unicode與UTF-8的區別做一個解釋,如果已瞭解,可跳過該部分。然後會分別對python2,3中的str、unicode進行講解。有問題的地方,歡迎交流。
Unicode與UTF-8
- Unicode 是「字符集」
UTF-8 是「編碼規則」
字符集:爲每一個「字符」分配一個唯一的 ID(學名爲碼位 / 碼點 / Code Point)
- 編碼規則:將「碼位」轉換爲字節序列的規則(編碼/解碼 可以理解爲 加密/解密 的過程)
廣義的 Unicode 是一個標準,定義了一個字符集以及一系列的編碼規則,即 Unicode 字符集和 UTF-8、UTF-16、UTF-32 等等編碼……
Unicode 字符集爲每一個字符分配一個碼位,例如「知」的碼位是 30693,記作 U+77E5(30693 的十六進制爲 0x77E5)。
U+ 0000 ~ U+ 007F: 0XXXXXXX
U+ 0080 ~ U+ 07FF: 110XXXXX 10XXXXXX
U+ 0800 ~ U+ FFFF: 1110XXXX 10XXXXXX 10XXXXXX
U+10000 ~ U+1FFFF: 11110XXX 10XXXXXX 10XXXXXX 10XXXXXX
根據上表中的編碼規則,之前的「知」字的碼位 U+77E5 屬於第三行的範圍:
7 7 E 5
0111 0111 1110 0101 二進制的 77E5
--------------------------
0111 011111 100101 二進制的 77E5
1110XXXX 10XXXXXX 10XXXXXX 模版(上表第三行)
11100111 10011111 10100101 代入模版
E 7 9 F A 5
這就是將 U+77E5 按照 UTF-8 編碼爲字節序列 E79FA5 的過程。
python2中的str和unicode
str與unicode
Python2中:
str格式本質含義是“某種編碼格式”,絕大多數情況下,被引號框起來的字符串,就是str,它本身存儲的就是字節碼(bytes)。
>>> s = "我愛我的祖國" >>> s '\xce\xd2\xb0\xae\xce\xd2\xb5\xc4\xd7\xe6\xb9\xfa'
那麼這個字節碼是什麼格式的。
如果這段代碼是在解釋器上輸入的,那麼這個s的格式就是解釋器的編碼格式,對於windows的cmd而言,就是gbk。
如果將段代碼是保存後才執行的,比如存儲爲utf-8,那麼在解釋器載入這段程序的時候,就會將s初始化爲utf-8編碼。
下面是我在cmd中的嘗試
>>> s = "我愛我的祖國" >>> print chardet.detect(s) {'confidence': 0.99, 'language': 'Chinese', 'encoding': 'GB2312'}
在我的測試過程中,有些中文會被識別成別的編碼,不是GB2312,如下識別成俄語編碼,這個可能跟windows有關。
>>> s = "加油" >>> print chardet.detect(s) {'confidence': 0.7679697235616183, 'language': 'Russian', 'encoding': 'KOI8-R'} >>> s = "中國" >>> print chardet.detect(s) {'confidence': 0.7679697235616183, 'language': 'Russian', 'encoding': 'IBM855'}
unicode類型的含義就是“用unicode編碼的字符串”。unicode()是單獨的,不像str()是byte類型。
>>> s = u"我愛我的祖國" >>> s u'\u6211\u7231\u6211\u7684\u7956\u56fd' >>> print type(s) <type 'unicode'> >>> print chardet.detect(s) TypeError: Expected object of type bytes or bytearray, got: <type 'unicode'>
引號前面的u表示這裏創建的是一個Unicode字符串
Python在進入2.0版後正式定義了了Unicode字符串這個奇怪的特性,目的就是爲了處理太多種語言編碼的文本。從那時開始,Python語言中的字符串類型就分爲兩種:一種是傳統的Python字符串(各種花樣編碼),另一種則是新出現的Unicode。
encode與decode
下面的測試,都將在代碼存儲爲utf-8後,再由解釋器執行,也就是說str將初始化爲utf-8編碼
在python2中的解碼(decode)、編碼(encode)操作如下:
import chardet
s = "我愛我的祖國"
print type(s)
print chardet.detect(s)
u = s.decode("UTF-8")
print type(u)
s = u.encode("gbk")
print type(s)
print chardet.detect(s)
'''
<type 'str'>
{'confidence': 0.99, 'language': '', 'encoding': 'utf-8'}
<type 'unicode'>
<type 'str'>
{'confidence': 0.99, 'language': 'Chinese', 'encoding': 'GB2312'}
'''
從輸出可看出,s爲utf-8編碼的字符串,用decode()將s解碼爲unicode對象。
對解碼後的unicode對象進行編碼,用encode()將s編碼爲utf-8對象。
那麼,再看下,下面這個情況,s作爲字符串,不僅有decode(),還有encode()。
s = "我愛我的祖國"
print hasattr(s, "decode")
print hasattr(s, "encode")
'''
True
True
'''
decode()我們是已經使用過了,將某種編碼的字符串解碼成unicode對象。那麼是否能直接將字符串改成另外一種編碼呢?這就是str的encode(),爲我們省去了decode()的過程。但這裏也有一個坑!
s = "我愛我的祖國"
s = s.encode("gbk")
'''
UnicodeDecodeError: 'ascii' codec can't decode byte 0xce in position 0: ordinal not in range(128)
'''
嘗試將utf-8的str直接變成gbk編碼,報錯了,報錯信息翻譯如下:
‘ascii’編解碼器無法解碼位置0的字節0xce:序號不在範圍(128)
可以看出,程序嘗試用ascii碼對s進行解碼,之所以會使用ascii,這與系統的默認編碼有關:
import sys
print sys.getdefaultencoding()
'''
ascii
'''
那麼,我們將sys的默認編碼設置成utf-8,就可以正常運行了
import sys
reload(sys)
sys.setdefaultencoding('utf-8')
s = "我愛我的祖國"
print chardet.detect(s)
s = s.encode("gbk")
print chardet.detect(s)
'''
{'confidence': 0.99, 'language': '', 'encoding': 'utf-8'}
{'confidence': 0.99, 'language': 'Chinese', 'encoding': 'GB2312'}
'''
其實s.encode(“gbk”)就相當於
s.decode(defaultencoding),encode('utf-8')
python3中的str和unicode
str格式的定義變更爲”Unicode類型的字符串“,也就是說在默認情況下,被引號框起來的字符串,是使用Unicode編碼的。也就是說unicode類型在python3中沒有了,python3中的str就相當於python2中的unicode。
在python3裏,str將不再是python2中的字節碼,不能作爲chardet.detect的參數。
import chardet s = "我愛我的祖國" print (type(s)) print (chardet.detect(s)) ''' <class 'str'> TypeError: Expected object of type bytes or bytearray, got: <class 'str'> '''
str也沒有了解碼方法decode()
s = "我愛我的祖國" print (hasattr(s, "decode")) print (hasattr(s, "encode")) ''' False True '''
python3中源碼文件默認使用utf-8編碼,使得以下代碼是合法的
>>> 中國 = 'china' >>>print(中國) china
下面這個 不知道該怎麼解釋,不是以unicode編碼的形式輸出了
>>> s = "我愛我的祖國" >>> s '我愛我的祖國'
而“不是Unicode的某種編碼格式”,比如UTF-8、GBK,這些編碼方式被定義爲了bytes,這裏的bytes和python2中的str有很多相似的地方。