從Python的角度來看編碼與解碼
導語:
Python2和Python3中,因為默認字符集的不同而造成的麻煩,簡直是程序員的夢魘!要徹底告別這個麻煩,就需要從本質上來理解編碼和解碼。
為什麽要有編碼?
對於不會英文的中國人來說,將英文翻譯成中文,這個就叫做解碼;而將中文翻譯成英文,自然就是編碼了!
這個道理在計算機中同樣適用。
計算機只能識別0和1,任何文字對於計算機來說,就是0和1的排列組合。但是我們人類哪看得懂這種0和1的排列組合!
自然就需要將0和1的文字轉換為我們能看得懂的文字,比如中文,英文等。
而這種0和1到文字的映射,就叫做解碼;文字到0和1的映射自然就是編碼了!
有了映射關系,自然就需要有個類似“表格”的東西,來記錄這種映射關系了!這個“表格”就是我們常說的字符集(也叫編碼集,簡稱編碼),比如ascii編碼,utf-8字符集。
習慣上,我們把人類能看懂的文字叫做字符,對應的計算機能看得懂的文字叫做字符編號(也就是字節流),而字符集就是這種字符和字符編號的映射。
為什麽會有亂碼?
上面我們說到,字符集有多種,那麽問題來了。
比如,在編碼時,我采用utf-8字符集進行文字到0和1的映射,但是解碼時,又采用gbk的字符集進行0和1到文字的映射,
2種字符集的映射規則是不一樣的!結果自然就是亂碼!
這就像我買了張三家的鎖,卻用李四家配的鑰匙來開門,能開門就見鬼了!
(其實對於計算機來說,根本沒有亂碼這一說法!對於特定的字符,0和1的排列組合是唯一不變的,變得是映射後的文字。)
關於文件編碼
不知道有沒有人有過這樣的疑問,CPU一個只能運算0和1的器件,是如何能夠處理文本、圖片、視頻和聲音這些資源的?
其實這就要講到文件編碼。
以下文字來自知乎網友的解答:
作者:DJ Hitori
鏈接:https://www.zhihu.com/question/27805272/answer/74539468
來源:知乎
著作權歸作者所有。商業轉載請聯系作者獲得授權,非商業轉載請註明出處。
文字: 有若幹種標準可以用8~16位二進制數表示一個字符,比如用8位數表示英語字母數字的ASCII,用16位數表示幾乎所有語言所有字符的Unicode等。以ASCII為例,這個標準用01000001這個數表示大寫的A,於是某個文字處理軟件看到這個數字知道這是A,然後向一個字體文件詢問字母A長什麽樣,然後把它畫在屏幕上,你就看到了A。 圖像: 最簡單的格式叫“位圖”(BMP),用24位二進制數表示圖像中的一個點,這24位數中8位表示這個點有多少紅色,8位表示多少綠色,8位表示多少藍色,三種原色組合起來就可以表示幾乎所有的顏色。很多個24位數連在一起,就是很多個點,於是就組成了一幅圖像。除此以外還有其他格式以更少的數字表示同樣多的圖像內容,如JPG、PNG等。 聲音: 聲音是波,是數學上的連續函數,而計算機無法理解連續函數,所以每秒取樣44100次,把1秒的聲音變成44100個數字記錄下來,這是錄音的過程。回放時,把這些數字交給聲卡,聲卡控制喇叭按照這些數字表示的幅度震動,就發出了聲音。同樣,除了44100個數字(這是WAV格式)以外還有其他格式以更少的數字表示同樣多的聲音,如MP3、OGG等。 視頻: 既然有了圖像和聲音的標準,那麽每秒鐘24~60幅圖像再加上1秒鐘的聲音就組成了1秒鐘的視頻內容。不過這種做法的數據量異常龐大,所以沒人這麽幹,科學家們發明了各種編碼方式以非常非常少(相對於未壓縮)的數字表示同樣多的視頻內容,如H.264等。 各種軟件控制CPU按照各種標準理解了多媒體內容後,計算出屏幕上每一個點應該是什麽顏色(和位圖一樣),然後把這些計算結果交給顯卡,顯卡把這一堆數字表示的顏色畫到屏幕上,這叫做一次刷新。一般來說每秒刷新60次,這樣你就流暢地看到了你所打開的多媒體內容。
了解了編碼和解碼的大環境之後,再來看看Python中的編碼和解碼:
字符串類型
Python2中字符串類型有2種,unicode和str。
>>> s = ‘中國‘
>>> s
‘\xe4\xb8\xad\xe5\x9b\xbd‘
>>> u = u‘中國‘
>>> u
u‘\u4e2d\u56fd‘
>>> type(s)
<type ‘str‘>
>>> type(u)
<type ‘unicode‘>
Python3中字符串類型只有str一種。
>>> s = ‘中國‘
>>> s
‘中國‘
>>> type(s)
<class ‘str‘>
>>> u = u‘中國‘
>>> u
‘中國‘
>>> type(u)
<class ‘str‘>
默認字符集
Python2中默認字符集為ascii,其中默認中文字符集為utf-8。
>>> sys.getdefaultencoding()
‘ascii‘
Python3中,無論什麽文,默認字符集為utf-8。
>>> sys.getdefaultencoding()
‘utf-8‘
關於編碼和字節流
編碼的結果為字節流。
Python2,str即是字節流
>>> u.encode(‘gbk‘)
‘\xd6\xd0\xb9\xfa‘
>>> u.encode(‘utf-8‘)
‘\xe4\xb8\xad\xe5\x9b\xbd‘
>>> s
‘\xe4\xb8\xad\xe5\x9b\xbd‘
>>> s = b‘中國‘
>>> s
‘\xe4\xb8\xad\xe5\x9b\xbd‘
>>> type(s)
<type ‘str‘>
>>> bytes(s)
‘\xe4\xb8\xad\xe5\x9b\xbd‘
Python3,不能夠直接定義中文字節流
>>> s.encode(‘utf-8‘)
b‘\xe4\xb8\xad\xe5\x9b\xbd‘
>>> s = b‘中國‘
File "<stdin>", line 1
SyntaxError: bytes can only contain ASCII literal characters.
關於解碼
解碼的結果為str(python3)或者unicode(python2)
Python2
>>> s.decode(‘utf-8‘)
u‘\u4e2d\u56fd‘
>>> u
u‘\u4e2d\u56fd‘
Python3
>>> a
b‘\xe4\xb8\xad\xe5\x9b\xbd‘
>>> a.decode()
‘中國‘
檢測
可以通過isinstance()
來判斷類型
>>> isinstance(a,str)
True
>>> isinstance(b‘qq‘,str)
True
關於錯誤
UnicodeDecodeError: ‘utf-8‘ codec can‘t decode byte 0xd6 in position 0: invalid continuation byte
出現上述錯誤的原因在於:編碼時使用的字符集和解碼時使用的字符集不一致。
拓展:
\0x:當輸出的數轉換為16進制只有1位時,在前面補0,如 0a,其它情況按照實際情況輸出。
\x:按照輸出數轉換為16進制的實際位數輸出。
此外,小寫x和大寫X也有點區別,小寫的x輸出小寫符號的16進制,大寫X則輸出大寫的(主要針對ABCDEF這六位)
從Python的角度來看編碼與解碼