1. 程式人生 > >Python3中的bytes和str型別

Python3中的bytes和str型別

Python 3最重要的新特性之一是對字串和二進位制資料流做了明確的區分。文字總是Unicode,由str型別表示,二進位制資料則由bytes型別表示。Python 3不會以任意隱式的方式混用strbytes,你不能拼接字串和位元組流,也無法在位元組流裡搜尋字串(反之亦然),也不能將字串傳入引數為位元組流的函式(反之亦然)。

下面讓我們深入分析一下二者的區別和聯絡。

編碼發展的歷史

在談bytesstr之前,需要先說說關於編碼是如何發展的。。

在計算機歷史的早期,美國為代表的英語系國家主導了整個計算機行業,26個英文字母組成了多樣的英語單詞、語句、文章。因此,最早的字元編碼規範是ASCII碼,一種8位即1個位元組的編碼規範,它可以涵蓋整個英語系的編碼需要。

編碼是什麼?編碼就是把一個字元用一個二進位制來表示。我們都知道,所有的東西,不管是英文、中文還是符號等等,最終儲存在磁碟上都是01010101這類東西。在計算機內部,讀取和儲存資料歸根結底,處理的都是0和1組成的位元流。問題來了,人類看不懂這些位元流,如何讓這些010101對人類變得可讀呢?於是出現了字元編碼,它是個翻譯機,在計算機內部某個地方,透明的幫我們將位元流翻譯成人類可以直接理解的文字。對於一般使用者,不需要知道這個過程是什麼原理,是怎麼執行的。但是對於程式設計師卻是個必須搞清楚的問題。

ASCII編碼為例,它規定1個位元組8個位元位代表1個字元的編碼,也就是“00000000”這麼寬,一個一個位元組的解讀。例如:01000001表示大寫字母A,有時我們會“偷懶"的用65這個十進位制來表示A在ASCII

中的編碼。8個位元位,可以沒有重複的最多表示2的8次方(255)個字元。

後來,計算機得到普及,中文、日文、韓文等等國家的文字需要在計算機內表示,ASCII的255位遠遠不夠,於是標準組織制定出了叫做UNICODE的萬國碼,它規定任何一個字元(不管哪國的)至少以2個位元組表示,可以更多。其中,英文字母就是用2個位元組,而漢字是3個位元組。這個編碼雖然很好,滿足了所有人的要求,但是它不相容ASCII,同時還佔用較多的空間和記憶體。因為,在計算機世界更多的字元是英文字母,明明可以1個位元組就能夠表示,非要用2個。

於是UTF-8編碼應運而生,它規定英文字母系列用1個位元組表示,漢字用3個位元組表示等等。因此,它相容ASCII

,可以解碼早期的文件。UTF-8很快就得到了廣泛的應用。

在編碼的發展歷程中,我國還創造了自己的編碼方式,例如GBKGB2312BIG5。他們只侷限於在國內使用,不被國外認可。在GBK編碼中,中文漢字佔2個位元組。

bytes和str之間的異同

回到bytesstr的身上。bytes是一種位元流,它的存在形式是01010001110這種。我們無論是在寫程式碼,還是閱讀文章的過程中,肯定不會有人直接閱讀這種位元流,它必須有一個編碼方式,使得它變成有意義的位元流,而不是一堆晦澀難懂的01組合。因為編碼方式的不同,對這個位元流的解讀也會不同,對實際使用造成了很大的困擾。下面讓我們看看Python是如何處理這一系列編碼問題的:

>>> s = "中文"
>>> s
'中文'
>>> type(s)
<class 'str'>
>>> b = bytes(s, encoding='utf-8')
>>> b
b'\xe4\xb8\xad\xe6\x96\x87'     #\x 代表是十六進位制
>>> type(b)
<class 'bytes'>

 

從例子可以看出,s是個字串型別。Python有個內建函式bytes()可以將字串str型別轉換成bytes型別,b實際上是一串01的組合,但為了在ide環境中讓我們相對直觀的觀察,它被表現成了b'\xe4\xb8\xad\xe6\x96\x87'這種形式,開頭的b表示這是一個bytes型別。\xe4是十六進位制的表示方式,它佔用1個位元組的長度,因此”中文“被編碼成utf-8後,我們可以數得出一共用了6個位元組,每個漢字佔用3個,這印證了上面的論述。在使用內建函式bytes()的時候,必須明確encoding的引數,不可省略。

我們都知道,字串類str裡有一個encode()方法,它是從字串向位元流的編碼過程。而bytes型別恰好有個decode()方法,它是從位元流向字串解碼的過程。此之外,我們檢視Python原始碼會發現bytesstr擁有幾乎一模一樣的方法列表,最大的區別就是encodedecode

從實質上來說,字串在磁碟上的儲存形式也是01的組合,也需要編碼解碼。

如果,上面的闡述還不能讓你搞清楚兩者的區別,那麼記住下面兩幾句話:

  1. 在將字串存入磁碟和從磁碟讀取字串的過程中,Python自動地幫你完成了編碼和解碼的工作,你不需要關心它的過程。

  2. 使用bytes型別,實質上是告訴Python,不需要它幫你自動地完成編碼和解碼的工作,而是使用者自己手動進行,並指定編碼格式。

  3. Python已經嚴格區分了bytesstr兩種資料型別,你不能在需要bytes型別引數的時候使用str引數,反之亦然。這點在讀寫磁碟檔案時容易碰到。

在bytes和str的互相轉換過程中,實際就是編碼解碼的過程,必須顯式地指定編碼格式。

>>> b
b'\xe4\xb8\xad\xe6\x96\x87'
>>> type(b)
<class 'bytes'>
>>> s1 = str(b)
>>> s1
"b'\\xe4\\xb8\\xad\\xe6\\x96\\x87'"   #注意這裡
>>> type(s1)
<class 'str'>
>>> s1 = str(b, encoding='utf-8')
>>> s1
'中文'
>>> type(s1)
<class 'str'>

 

我們再把字串s1,轉換成gbk編碼的bytes型別:

>>> s1
'中文'
>>> type(s1)
<class 'str'>
>>> b =  bytes(s1, encoding='gbk')
>>> b

 



編碼可以將抽象字元以二進位制資料的形式表示,有很多編碼方法,如utf-8、gbk等,可以使用encode()函式對字串進行編碼,轉換成二進位制位元組資料,也可用decode()函式將位元組解碼成字串;用decode()函式解碼,可不要用指定編碼格式;

>>> a = 'hello world'
>>> type(a)
<class 'str'>
>>> a
'hello world'

 

a. 按utf-8的方式編碼,轉成bytes:以及解碼成字串

#a為串'hello world'
>>> b = a.encode(encoding='utf-8')
>>> type(b)
<class 'bytes'>
>>>
>>> b
b'hello world'
>>>
>>>
>>> c = b.decode(encoding='utf-8')
>>> type(c)
<class 'str'>
>>> c
'hello world'

 

b. 按gbk的方式編碼,轉成bytes:以及解碼成字串

>>> x = a.encode(encoding='gbk')
>>> type(x)
<class 'bytes'>
>>>
>>> x
b'hello world'
>>>
>>> y = x.decode()
>>> type(y)
<class 'str'>
>>>
>>> y
'hello world'

 



 

path="doc.txt"
 
f=open(path,'rb')
 
raw=f.read()
print(raw[:100])
print(len(raw))
print(type(raw))
 
raw1=str(raw)
print(raw1[:100])
print(len(raw1))
print(type(raw1))
 
tokens=word_tokenize(raw1)
print(len(tokens))
print(tokens[:20])

 

 

b'the rock is destined to be the 21st century\'s new " conan " and that he\'s going to make a splash eve'
626168
<class 'bytes'>
b'the rock is destined to be the 21st century\'s new " conan " and that he\'s going to make a splash
634857
<class 'str'>
115104
["b'the", 'rock', 'is', 'destined', 'to', 'be', 'the', '21st', 'century\\', "'s", 'new', '``', 'conan', '``', 'and', 'that', 'he\\', "'s", 'going', 'to']

from nltk import word_tokenize
 
path="doc.txt"  
  
f=open(path,'rb')  
  
raw=f.read()  
print(raw[:1000])  
print(len(raw))  
print(type(raw))  
raw=raw[:1000]
  
  
raw1= raw.decode(encoding='utf-8')  #在4000多的位置上有無法解碼的,也就是說解碼方式是有問題的,應該不是utf-8
print(raw1[:1000])  
print(len(raw1))  
print(type(raw1)) 

b'the rock is destined to be the 21st century\'s new " conan " and that he\'s going to make a splash even greater than arnold schwarzenegger , jean-claud van damme or steven segal . \nthe gorgeously elaborate continuation of " the lord of the rings " trilogy is so huge that a column of words cannot adequately describe co-writer/director peter jackson\'s expanded vision of j . r . r . tolkien\'s middle-earth . \neffective but too-tepid biopic\nif you sometimes like to go to the movies to have fun , wasabi is a good place to start . \nemerges as something rare , an issue movie that\'s so honest and keenly observed that it doesn\'t feel like one . \nthe film provides some great insight into the neurotic mindset of all comics -- even those who have reached the absolute top of the game . \noffers that rare combination of entertainment and education . \nperhaps no picture ever made has more literally showed that the road to hell is paved with good intentions . \nsteers turns in a snappy screenplay that curl'
626168
<class 'bytes'>
the rock is destined to be the 21st century's new " conan " and that he's going to make a splash even greater than arnold schwarzenegger , jean-claud van damme or steven segal . 
the gorgeously elaborate continuation of " the lord of the rings " trilogy is so huge that a column of words cannot adequately describe co-writer/director peter jackson's expanded vision of j . r . r . tolkien's middle-earth . 
effective but too-tepid biopic
if you sometimes like to go to the movies to have fun , wasabi is a good place to start . 
emerges as something rare , an issue movie that's so honest and keenly observed that it doesn't feel like one . 
the film provides some great insight into the neurotic mindset of all comics -- even those who have reached the absolute top of the game . 
offers that rare combination of entertainment and education . 
perhaps no picture ever made has more literally showed that the road to hell is paved with good intentions . 
steers turns in a snappy screenplay that curl
1000
<class 'str'>
189
['e', 'rock', 'is', 'destined', 'to', 'be', 'the', '21st', 'century', "'s", 'new', '``', 'conan', '``', 'and', 'that', 'he', "'s", 'going', 'to']

 

 

將a(字串)進行編碼,當a為中文時,將Unicode編碼為b'\\u4f60\\u597d',輸入b顯示時,python自動假設unicode字元的預設編碼方式是ASCII,但ASCII中沒有b'\\u4f60\\u597d',所以就直接輸出了。

當a為'hello'時,編碼為(也是數字),但print時可以按照ASCII的形式顯示bytes型別。

 

>>>a='你好'
>>>b=a.encode('unicode_escape')
>>>b
b'\\u4f60\\u597d'
 
 
>>>a='hello'
>>>b=a.encode('unicode_escape')
>>>b
b'hello'