前言
本文首先對(duì)Unicode與UTF-8的區(qū)別做一個(gè)解釋吵护,如果已了解,可跳過(guò)該部分表鳍。然后會(huì)分別對(duì)python2馅而,3中的str、unicode進(jìn)行講解譬圣。有問題的地方瓮恭,歡迎交流。
Unicode與UTF-8
- Unicode 是「字符集」
- UTF-8 是「編碼規(guī)則」
- 字符集:為每一個(gè)「字符」分配一個(gè)唯一的 ID(學(xué)名為碼位 / 碼點(diǎn) / Code Point)
- 編碼規(guī)則:將「碼位」轉(zhuǎn)換為字節(jié)序列的規(guī)則(編碼/解碼 可以理解為 加密/解密 的過(guò)程)
廣義的 Unicode 是一個(gè)標(biāo)準(zhǔn)厘熟,定義了一個(gè)字符集以及一系列的編碼規(guī)則屯蹦,即 Unicode 字符集和 UTF-8、UTF-16绳姨、UTF-32 等等編碼……
Unicode 字符集為每一個(gè)字符分配一個(gè)碼位登澜,例如「知」的碼位是 30693,記作 U+77E5(30693 的十六進(jìn)制為 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
根據(jù)上表中的編碼規(guī)則帖渠,之前的「知」字的碼位 U+77E5 屬于第三行的范圍:
7 7 E 5
0111 0111 1110 0101 二進(jìn)制的 77E5
--------------------------
0111 011111 100101 二進(jìn)制的 77E5
1110XXXX 10XXXXXX 10XXXXXX 模版(上表第三行)
11100111 10011111 10100101 代入模版
E 7 9 F A 5
這就是將 U+77E5 按照 UTF-8 編碼為字節(jié)序列 E79FA5 的過(guò)程。
python2中的str和unicode
str與unicode
Python2中:
-
str格式本質(zhì)含義是“某種編碼格式”竭宰,絕大多數(shù)情況下空郊,被引號(hào)框起來(lái)的字符串份招,就是str,它本身存儲(chǔ)的就是字節(jié)碼(bytes)狞甚。
>>> s = "我愛我的祖國(guó)" >>> s '\xce\xd2\xb0\xae\xce\xd2\xb5\xc4\xd7\xe6\xb9\xfa'
那么這個(gè)字節(jié)碼是什么格式的锁摔。
如果這段代碼是在解釋器上輸入的,那么這個(gè)s的格式就是解釋器的編碼格式哼审,對(duì)于windows的cmd而言谐腰,就是gbk。
如果將段代碼是保存后才執(zhí)行的涩盾,比如存儲(chǔ)為utf-8十气,那么在解釋器載入這段程序的時(shí)候,就會(huì)將s初始化為utf-8編碼春霍。
下面是我在cmd中的嘗試
>>> s = "我愛我的祖國(guó)" >>> print chardet.detect(s) {'confidence': 0.99, 'language': 'Chinese', 'encoding': 'GB2312'}
在我的測(cè)試過(guò)程中狰右,有些中文會(huì)被識(shí)別成別的編碼抢野,不是GB2312,如下識(shí)別成俄語(yǔ)編碼铲汪,這個(gè)可能跟windows有關(guān)瓤漏。
>>> s = "加油" >>> print chardet.detect(s) {'confidence': 0.7679697235616183, 'language': 'Russian', 'encoding': 'KOI8-R'} >>> s = "中國(guó)" >>> print chardet.detect(s) {'confidence': 0.7679697235616183, 'language': 'Russian', 'encoding': 'IBM855'}
-
unicode類型的含義就是“用unicode編碼的字符串”很魂。unicode()是單獨(dú)的样悟,不像str()是byte類型隙弛。
>>> s = u"我愛我的祖國(guó)" >>> 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'>
引號(hào)前面的u表示這里創(chuàng)建的是一個(gè)Unicode字符串
Python在進(jìn)入2.0版后正式定義了了Unicode字符串這個(gè)奇怪的特性,目的就是為了處理太多種語(yǔ)言編碼的文本喧伞。從那時(shí)開始走芋,Python語(yǔ)言中的字符串類型就分為兩種:一種是傳統(tǒng)的Python字符串(各種花樣編碼),另一種則是新出現(xiàn)的Unicode絮识。
encode與decode
下面的測(cè)試绿聘,都將在代碼存儲(chǔ)為utf-8后嗽上,再由解釋器執(zhí)行次舌,也就是說(shuō)str將初始化為utf-8編碼
在python2中的解碼(decode)、編碼(encode)操作如下:
import chardet
s = "我愛我的祖國(guó)"
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對(duì)象。
對(duì)解碼后的unicode對(duì)象進(jìn)行編碼浅萧,用encode()將s編碼為utf-8對(duì)象逐沙。
那么,再看下洼畅,下面這個(gè)情況吩案,s作為字符串,不僅有decode()帝簇,還有encode()徘郭。
s = "我愛我的祖國(guó)"
print hasattr(s, "decode")
print hasattr(s, "encode")
'''
True
True
'''
decode()我們是已經(jīng)使用過(guò)了靠益,將某種編碼的字符串解碼成unicode對(duì)象。那么是否能直接將字符串改成另外一種編碼呢残揉?這就是str的encode()胧后,為我們省去了decode()的過(guò)程。但這里也有一個(gè)坑抱环!
s = "我愛我的祖國(guó)"
s = s.encode("gbk")
'''
UnicodeDecodeError: 'ascii' codec can't decode byte 0xce in position 0: ordinal not in range(128)
'''
嘗試將utf-8的str直接變成gbk編碼壳快,報(bào)錯(cuò)了,報(bào)錯(cuò)信息翻譯如下:
'ascii'編解碼器無(wú)法解碼位置0的字節(jié)0xce:序號(hào)不在范圍(128)
可以看出镇草,程序嘗試用ascii碼對(duì)s進(jìn)行解碼眶痰,之所以會(huì)使用ascii,這與系統(tǒng)的默認(rèn)編碼有關(guān):
import sys
print sys.getdefaultencoding()
'''
ascii
'''
那么梯啤,我們將sys的默認(rèn)編碼設(shè)置成utf-8凛驮,就可以正常運(yùn)行了
import sys
reload(sys)
sys.setdefaultencoding('utf-8')
s = "我愛我的祖國(guó)"
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'}
'''
其實(shí)s.encode("gbk")就相當(dāng)于
s.decode(defaultencoding),encode('utf-8')
python3中的str和unicode
-
str格式的定義變更為”Unicode類型的字符串“,也就是說(shuō)在默認(rèn)情況下条辟,被引號(hào)框起來(lái)的字符串黔夭,是使用Unicode編碼的。也就是說(shuō)unicode類型在python3中沒有了羽嫡,python3中的str就相當(dāng)于python2中的unicode本姥。
在python3里,str將不再是python2中的字節(jié)碼杭棵,不能作為chardet.detect的參數(shù)婚惫。
import chardet s = "我愛我的祖國(guó)" print (type(s)) print (chardet.detect(s)) ''' <class 'str'> TypeError: Expected object of type bytes or bytearray, got: <class 'str'> '''
str也沒有了解碼方法decode()
s = "我愛我的祖國(guó)" print (hasattr(s, "decode")) print (hasattr(s, "encode")) ''' False True '''
python3中源碼文件默認(rèn)使用utf-8編碼,使得以下代碼是合法的
>>> 中國(guó) = 'china' >>>print(中國(guó)) china
下面這個(gè) 不知道該怎么解釋魂爪,不是以u(píng)nicode編碼的形式輸出了
>>> s = "我愛我的祖國(guó)" >>> s '我愛我的祖國(guó)'
而“不是Unicode的某種編碼格式”先舷,比如UTF-8、GBK滓侍,這些編碼方式被定義為了bytes蒋川,這里的bytes和python2中的str有很多相似的地方。