1. 程式人生 > >從Python的角度來看編碼與解碼

從Python的角度來看編碼與解碼

異常 字符 default 疑問 習慣 中文字符集 nbsp prompt ans

導語:

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(bqq,str)
True

關於錯誤

UnicodeDecodeError: utf-8 codec cant decode byte 0xd6 in position 0: invalid continuation byte

出現上述錯誤的原因在於:編碼時使用的字符集和解碼時使用的字符集不一致。

拓展:

\0x:當輸出的數轉換為16進制只有1位時,在前面補0,如 0a,其它情況按照實際情況輸出。

\x:按照輸出數轉換為16進制的實際位數輸出。

此外,小寫x和大寫X也有點區別,小寫的x輸出小寫符號的16進制,大寫X則輸出大寫的(主要針對ABCDEF這六位)

從Python的角度來看編碼與解碼