天天看點

淺析Python3中的bytes和str

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

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

編碼發展的曆史

在談bytes和str之前,需要先說說關于編碼是如何發展的。。

在計算機曆史的早期,美國為代表的英語系國家主導了整個計算機行業,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很快就得到了廣泛的應用。

在編碼的發展曆程中,我國還創造了自己的編碼方式,例如GBK,GB2312,BIG5。他們隻局限于在國内使用,不被國外認可。在GBK編碼中,中文漢字占2個位元組。

bytes和str之間的異同

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

>>> s = "中文"
>>> s
'中文'
>>> type(s)
<class 'str'>
>>> b = bytes(s, encoding='utf-8')
>>> b
b'\xe4\xb8\xad\xe6\x96\x87'
>>> 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源碼會發現bytes和str擁有幾乎一模一樣的方法清單,最大的差別就是encode和decode。

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

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

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

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

Python已經嚴格區分了bytes和str兩種資料類型,你不能在需要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
b'\xd6\xd0\xce\xc4'           

複制

原文出處:https://www.cnblogs.com/chownjy/p/6625299.html