歡迎關注公衆號:python數據科學家php
【要點搶先看】python
1.python中編、解碼的本質是文本字符串和字節字符串的相互轉換
2.python中編、解碼方法舉例及過程解析
3.unicode、latin-一、ASCII編碼方式的兼容性問題
4.讀取二進制文件bash
上一集講清楚字符編碼的基礎概念後,我相信這一集再來介紹python中的字符編碼就會容易的多。網絡
經過上一集咱們知道,ASCII碼(包括其最多見的超集Latin-1)依賴這樣的一個假設,即每個字符與一個字節相匹配,因爲存在太多的字符,所以不可避免的會出現問題,Unicode字符集經過使用4個字節來表示1個字符,則解決了該問題。app
首先來介紹一下Python中的兩種字符串:函數
Python中有兩種字符串:文本字符串和字節字符串。其中文本字符串類型被命名爲str,內部採用Unicode字符集(兼容ASCII碼),而字節字符串則直接用來表示原始的字節序列(用print函數來打印字節字符串時,若字節在ascii碼範圍內,則顯示爲ascii碼對應的字符,其他的則直接顯示爲16進制數),該類型被命名爲bytes。ui
看一個簡單的例子:編碼
s = 'apple'
b = b'apple'
print(b)
print(type(b))
print(s)
print(type(s))
b'apple'
<class 'bytes'>
apple
<class 'str'>
複製代碼
再近距離的看看bytes類型字節字符串,本質上它就是一串單字節16進制數spa
b = b'apple'
print(b[0])
print(b[1:])
print(list(b))
97
b'pple'
[97, 112, 112, 108, 101]
複製代碼
【妹子說】那這和編碼、解碼有何關聯呢?code
從本質上來講,編碼和解碼就是str和bytes這兩種字符串類型之間的互相轉換。
str包含一個encode方法,使用特定編碼將該字符串其轉換爲一個bytes,這稱之爲編碼。bytes類包含了一個decode方法,也接受一個編碼做爲單個必要參數,並返回一個str,這稱之爲解碼。這種轉換操做是顯式的操做,且必須根據數據被編碼時採用的編碼類型進行解碼。
首先說說編碼,即將unicode的str文本字符串轉換爲bytes的字節字符串,能夠顯式的傳入指定編碼(通常來講採用utf-8編碼),或使用平臺的默認編碼。
s = 'π排球の'
b1 = s.encode('utf-8')
b2 = s.encode()
print(b1)
print(b2)
b'\xcf\x80\xe6\x8e\x92\xe7\x90\x83\xe3\x81\xae'
b'\xcf\x80\xe6\x8e\x92\xe7\x90\x83\xe3\x81\xae'
複製代碼
那麼咱們看看,在不寫編碼的時候,平臺默認的編碼方式究竟是什麼
import sys
print(sys.platform)
print(sys.getdefaultencoding())
win32
utf-8
複製代碼
能夠看出我這個平臺默認選擇的是utf-8編碼方式。
接下來咱們來比較一下unicode、latin-一、ASCII編碼方式的兼容性問題:
首先,非ASCII字符沒法使用ASCII編碼轉換成字節字符串
s = 'π排球の'
b = s.encode('ascii')
Traceback (most recent call last):
File "E:/12homework/12homework.py", line 2, in <module>
b = s.encode('ascii')
UnicodeEncodeError: 'ascii' codec can't encode characters in position 0-3:
ordinal not in range(128)
複製代碼
其次,Latin-1和unicode編碼方式不兼容。
例如,重音字符會在latin-1字符集和unicode字符集中同時存在,可是經過latin-1和unicode編碼方式編出來的字節流是不同的,注意,雖然unicode字符集是包含了latin-1字符集,可是不表明utf-8編碼方式兼容latin-1編碼方式。由於unicode字符集中除了ascii字符集外,都是採用多字節的編碼方式,而latin-1一概採用單字節的方式
s = 'Äè'
print(s.encode('utf-8'))
print(s.encode('latin-1'))
b'\xc3\x84\xc3\xa8'
b'\xc4\xe8'
複製代碼
只有ascii字符集中的字符,三種編碼方式獲得的結果才徹底一致。對unicode進行編碼的時候,針對常規的7位ASCII文本,因爲utf-8以及latin-1編碼方式都是兼容ASCII的,因此結果都是同樣的。
s = 'abc'
print(s.encode('utf-8'))
print(s.encode('latin-1'))
print(s.encode('ascii'))
b'abc'
b'abc'
b'abc'
複製代碼
【妹子說】那對應的,再來談談decode解碼方法吧。
將bytes類型字符串轉換成str類型的unicode文本字符串也是同樣,要麼指定編碼參數,要麼使用平臺的默認參數。這個例子中,咱們要操做的字節字符串b是經過utf-8編碼方式對文本字符串'π排球の'編碼而造成的。
b = b'\xe6\x8e\x92\xe7\x90\x83'
s1 = b.decode(encoding='utf-8')
s2 = b.decode()
s3 = b.decode(encoding='latin-1')
print(s1)
print(s2)
print(s3)
排球
排球
排çƒ
複製代碼
值得注意的是,最後一行代碼想經過latin-1解碼字節字符串,因爲字節字符串是經過utf-8編碼造成,所以這樣解碼造成獲得的只能是亂碼。
Utf-8編碼是用兩個字節來表示非ASCII的高128字符,而latin-1則是用一個字節來一一對應
【妹子說】計算機用二進制來存儲信息,而卻能在各類應用中顯示咱們須要的文字,這應該是字符編、解碼的應用吧。
很對,下面咱們來講說文本文件讀取時的編、解碼問題
當一個文件以文本模式打開的時候,被讀取的二進制存儲數據(也就是存儲的字節字符串)會自動被解碼(依據顯式提供的編碼名稱或平臺默認的編碼名稱),而且將其返回爲一個str。寫入文件時,會接受一個str,而且將其傳輸到文件以前自動編碼成字節字符串以供磁盤存儲。
當一個文件以二進制模式打開時,須要在open方法的模式字符串參數裏添加一個b,此時讀取的數據不會以任何方式解碼,而是直接返回其原始內容,即一個bytes對象;寫入文件時,接受一個bytes對象,而且將其傳送到文件中且不進行修改。
在讀取文本文件的時候,若是open函數沒有聲明他們如何編碼,python3會因其所運行的系統而選取默認的編碼方式,默認狀況下,python3 指望文件使用 utf-8進行編碼。但因爲文件並不老是在同一個系統中被保存和打開,所以會帶來亂碼的風險,因此咱們須要顯式的指定編碼。
補充的說明一下,能夠很簡單的進行一個分類:處理圖像文件、設備數據流等,可使用bytes和二進制模式文件處理;而若是要處理的內容實質是文本的內容,例如程序輸出、HTML、國際化文本或CSV或XML文件,則可能要使用str和文本模式文件
例如,咱們先把AÄBèC用UTF-8編碼後存入utf-8data文件,再來讀取他,具體看看這裏是如何實現的。
s = 'AÄBèC'
with open('utf-8data','w',encoding='utf-8') as f:
f.write(s)
with open('utf-8data','r',encoding='utf-8') as f:
u_str = f.read()
print(u_str)
AÄBèC
複製代碼
這裏用到的文件讀寫的方法後面的章節會詳細介紹,如今知道他是什麼就行了。
以二進制的形式讀取文件。
還有一種咱們以前介紹過的用法,文本字符串在存儲到磁盤的時候會編碼成字節字符,所以咱們也能夠先以字節字符串的形式從文件中將其讀取,而後再進行解碼。
這樣作的緣由有二,一種是所接收的多是非文本數據,如一個圖像文件;另外一個潛在緣由是沒法肯定所讀取文本文件的編碼,可能須要依據其餘信息再肯定:
with open('utf-8data', 'rb') as f:
byte_str = f.read()
print(byte_str)
print(byte_str.decode(encoding='utf-8'))
b'A\xc3\x84B\xc3\xa8C'
AÄBèC
複製代碼
字符串編、解碼在python中很重要,特別是在網絡爬蟲等網絡應用程序中,在後面的實際應用中會感覺到他的做用會愈來愈明顯。
【妹子說】這一集的內容不少,細緻剖析了python中的兩種字符串類型和編、解碼的處理方法。再結合以前的三集,就能從基本使用、字符編、解碼的維度閉環出一個完整的知識網絡了,收穫很大。
公衆號二維碼:python數據科學家: