談談python2,3中的str、unicode

前言

本文首先對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有很多相似的地方。

參考

  1. 知乎(Unicode 和 UTF-8 有何區別?)
  2. 知乎(Python2和3中關於str和unicode以及UTF-8的更改到底是什麼意思?)
  3. Python中的str與unicode處理方法
  4. 菜鳥教程(字符串)
  5. 菜鳥教程(Python2.x與3.x版本區別)
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章