一. 什么是編碼
將明文轉(zhuǎn)換為計算機(jī)可以識別的的編碼文本稱為“編碼”涂滴, 反之從計算機(jī)可識別的編碼文本轉(zhuǎn)回明文為“解碼”
1. ASCII
計算機(jī)上的數(shù)據(jù)都是以二進(jìn)制的形式存儲的,1個字節(jié)(8比特)可以表示256種狀態(tài),英文只有26個字符寸癌,再加上一些特殊字符,使用128個就夠了弱贼,計算機(jī)就可以使用127個不同字節(jié)來存儲英語文字蒸苇,這就是ASCII編碼
最開始的時候8位中的最高位是沒有用到的,后來為了表示拉丁文吮旅,將最高位用上形成擴(kuò)展ASCII編碼溪烤,一個字節(jié)就用滿了味咳。
2. GB2312
計算機(jī)進(jìn)入中國后,無法顯示中文檬嘀,一個自己已經(jīng)被占滿了槽驶,我國重新制定了一個編碼表,將擴(kuò)展的第八位對應(yīng)的拉丁文全部刪掉鸳兽,規(guī)定一個小于127的字符與原來的意義相同掂铐,當(dāng)兩個大于127的字符鏈接在一起的時候,就表示一個漢字揍异,前面一個字節(jié)為高字節(jié)(0xA1-0xF7)全陨,后面一個字節(jié)為低字節(jié)(0xA1-0xFE),這樣可以表示7000多個漢字衷掷,這種編碼叫做GB2312辱姨。GB2312是對ASCII的中文擴(kuò)展
3. GBK和GB18030
由于漢字的數(shù)量太大,GB2312不能滿足需求戚嗅,后來規(guī)定只要第一個字節(jié)大于127就固定表示一個漢字雨涛,不管后面的是不是擴(kuò)展字符集里面的內(nèi)容,擴(kuò)展后的編碼成為GBK, GBK包括了GB2312的所有內(nèi)容渡处,同時增加了近20000個新的漢字(包括繁體)和符號
4. Unicode
在Unicode出現(xiàn)之前镜悉,每個國家都搞自己的編碼,彼此之間互不支持医瘫,帶來很多麻煩,國際標(biāo)標(biāo)準(zhǔn)組織提出來一個統(tǒng)一的編碼標(biāo)準(zhǔn):Unicode
Unicode用兩個字符來表示一個字符旧困,可以提供65535種字符醇份,足夠覆蓋世界上的所有符號
5. utf-8
Unicode的出現(xiàn),提供了統(tǒng)一的標(biāo)準(zhǔn)吼具,但對于英文世界的國家來說僚纷,一個字節(jié)完全夠用,如果使用Unicode會浪費(fèi)大量空間拗盒,為了解決這個問題提出了utf-8怖竭,一種針對Unicode的可變長度字符編碼,可以使用1-4個字節(jié)表示一個符號陡蝇,根據(jù)不同的符號變化字節(jié)長度痊臭,當(dāng)字符在ASCII編碼范圍時,用一個字節(jié)表示登夫,兼用ASCII广匙。
使用這樣的編碼的好處是,雖然內(nèi)存匯總的數(shù)據(jù)都是Unicode恼策,但當(dāng)數(shù)據(jù)保存到磁盤或者用于網(wǎng)絡(luò)傳輸時鸦致,使用utf-8會節(jié)省更多的流量和硬盤空間。
Unicode和utf-8的關(guān)系:Unicode是內(nèi)存編碼表示方案(規(guī)范),而utf-8是如何保存和傳輸U(kuò)nicode的方案(實(shí)現(xiàn))
二. Python2 中的sting編碼
在Python2中分唾,有兩種字符串類型:str類型和Unicode類型抗碰。這兩個類型只是Python定義的兩個名字,關(guān)鍵還要看這兩種數(shù)據(jù)類型在內(nèi)存中的存儲方式是什么绽乔。
>>> s1 = '中'
>>> print type(s1)
<type 'str'>
>>> print repr(s1)
'\xd6\xd0'
>>> s2 = u'中'
>>> print type(s2)
<type 'unicode'>
>>> print repr(s2)
u'\u4e2d'
由此可以看出str和Unicode分別存儲的是字節(jié)數(shù)據(jù)和Unicode數(shù)據(jù).那么這兩種數(shù)據(jù)之間的關(guān)系是什么樣的弧蝇?
>>> s1 = u'中'
>>> print repr(s1)
u'\u4e2d'
>>>
>>> b = s1.encode('utf-8')
>>> print b
中
>>> print type(b)
<type 'str'>
>>> print repr(b)
'\xe4\xb8\xad'
>>>
>>> s2 = '中國'
>>> u = s2.decode('utf-8')
>>> print u
中國
>>> print type(u)
<type 'unicode'>
>>> print repr(u)
u'\u4e2d\u56fd'
s2的“中國”在Windows系統(tǒng)中需要使用gbk
來解碼, 無論utf-8
還是gbk
都只是一種編碼編碼規(guī)則迄汛,一種把Unicode數(shù)據(jù)編碼成字節(jié)數(shù)據(jù)的規(guī)則捍壤。所以utf-8
編碼的字節(jié)一定要utf-8
解碼,否則亂碼或者報錯鞍爱。
# -*-coding:utf-8-*-
print '中國' # 中國
print repr('中國') # '\xe4\xb8\xad\xe5\x9b\xbd'
print (u'hello' + 'world') # helloworld
print (u'中國' + '人民') #UnicodeDecodeError:
#'ascii' codec can't decode byte 0xe4
#in position 0: ordinal not in range(128)
Python 2 悄悄掩蓋掉了 byte 到 unicode 的轉(zhuǎn)換鹃觉,只要數(shù)據(jù)全部是 ASCII 的話,所有的轉(zhuǎn)換都是正確的睹逃,一旦一個非 ASCII 字符偷偷進(jìn)入你的程序盗扇,那么默認(rèn)的解碼將會失效,從而造成 UnicodeDecodeError 的錯誤沉填。py2編碼讓程序在處理 ASCII 的時候更加簡單疗隶。付出的代價就是在處理非 ASCII 的時候?qū) ?/p>
三. Python3中的string編碼
python3 renamed the unicode type to str ,the old str type has been replaced by bytes.
** py3也有兩種數(shù)據(jù)類型:str和bytes; str類型存unicode數(shù)據(jù)翼闹,bytse類型存bytes數(shù)據(jù)斑鼻,與py2比只是換了一下名字而已。**
python3中將utf-8
或者gbk
等編碼的字節(jié)數(shù)據(jù)轉(zhuǎn)為Python3中的str
類型猎荠, utf-8編碼的bytes <---> str
python2中將 utf-8
或者gbk
等編碼的str編解碼為Python2中的Unicode
坚弱, utf-8編碼的str <---> unicode
Python3中的編碼思想
Python 3清晰地將文本和二進(jìn)制數(shù)據(jù)區(qū)分開了,不會對bytes字節(jié)串進(jìn)行自動解碼关摇。文本總是Unicode荒叶,由str類型表示,二進(jìn)制數(shù)據(jù)則由bytes類型表示输虱。Python 3不會以任意隱式的方式混用str和bytes些楣,將兩者明確地區(qū)分開∠芏茫基于此愁茁,Python3中不能拼接字符串和字節(jié)包,也不可以在字節(jié)包里搜索字符串(反之亦然)横堡,也不能向使用字符串參數(shù)的函數(shù)中傳入字節(jié)包參數(shù)(反之亦然)埋市。
四. 文件存儲讀取過程中的編碼問題
對于文本編輯器word等軟件,當(dāng)我們在這些軟件上編輯文字的時候命贴,無論是什么語言的文字或符號道宅,計算機(jī)都是無法識別的食听。
那么在保存之前數(shù)據(jù)是通過什么形式存在內(nèi)存的呢?
是unicode數(shù)據(jù)污茵,為什么要存unicode數(shù)據(jù)樱报,這是因?yàn)闊o論世界上的任何字符它都有唯一編碼對應(yīng),兼容性是最好的泞当。
當(dāng)我們保存了存到磁盤上的數(shù)據(jù)又是什么呢迹蛤?
是通過某種編碼方式編碼的bytes字節(jié)串。比如utf8---一種可變長編碼襟士,很好的節(jié)省了空間盗飒;還可以是gbk等編碼方式。
在我們的文本編輯器軟件都有默認(rèn)的保存文件的編碼方式陋桂,比如utf-8逆趣,gbk等。當(dāng)我們保存的時候嗜历,這些編輯軟件已經(jīng)"默默地"做了編碼工作宣渗。
那當(dāng)我們再打開這個文件時,軟件又默默地給我們做了解碼的工作梨州,將數(shù)據(jù)再解碼成unicode,然后就可以呈現(xiàn)明文給用戶了痕囱!
所以,unicode是離用戶更近的數(shù)據(jù)暴匠,bytes是離計算機(jī)更近的數(shù)據(jù)鞍恢。
編碼與程序運(yùn)行的關(guān)系
編寫Python代碼一般會用到sublime,pycharm每窖,vim等軟件有序。而代碼文件的創(chuàng)建、保存岛请、執(zhí)行等過程就伴隨著編解碼流程。下面以pycharm為例介紹這一過程警绩。
使用pycharm創(chuàng)建hello.py文件崇败,當(dāng)我們保存的的時候,hello.py文件就以pycharm默認(rèn)的編碼方式保存到了磁盤肩祥;關(guān)閉文件后再打開后室,pycharm就再以默認(rèn)的編碼方式對該文件打開后讀到的內(nèi)容進(jìn)行解碼,轉(zhuǎn)成unicode到內(nèi)存我們就看到了我們的明文混狠;
而如果我們點(diǎn)擊運(yùn)行按鈕或者在命令行運(yùn)行該文件時岸霹,Python解釋器這個軟件就會被調(diào)用,打開文件将饺,然后將存儲在磁盤上的bytes數(shù)據(jù)解碼成unicode數(shù)據(jù)贡避,這個過程和編輯器是一樣的痛黎,不同的是解釋器會再將這些unicode數(shù)據(jù)翻譯成C代碼再轉(zhuǎn)成二進(jìn)制的數(shù)據(jù)流,最后通過控制操作系統(tǒng)調(diào)用cpu來執(zhí)行這些二進(jìn)制數(shù)據(jù)刮吧,整個過程才算結(jié)束湖饱。
py2默認(rèn)ASCII碼,py3默認(rèn)的utf8杀捻,可以通過如下方式查詢
import sys
print(sys.getdefaultencoding())
使用Python2需要在編碼文件頭加一行#-*-coding:utf-8-*-
井厌,是因?yàn)槿绻鹥y2解釋器去執(zhí)行一個utf8編碼的文件,就會以默認(rèn)地ASCII去解碼utf8致讥,一旦程序中有中文仅仆,自然就解碼錯誤了,所以我們在文件開頭位置聲明一下告訴解釋器不要以默認(rèn)的編碼方式去解碼這個文件垢袱,而是以utf8來解碼墓拜。
而Python3的解釋器因?yàn)槟J(rèn)utf8編碼,不存在這樣的問題惶桐。