由于每個國家都有自己的字元,是以其對應關系也涵蓋了自己國家的字元
- ASCII 占1個位元組,隻支援英文
- GB2312 占2個位元組,支援6700+漢字
- GBK GB2312的更新版,支援21000+漢字
- Shift-JIS 日本字元
- ks_c_5601-1987 南韓編碼
- TIS-620 泰國編碼
但是以上編碼都存在局限性,即:僅涵蓋本國字元,無其他國家字元的對應關系。應運而生出現了萬國碼,他涵蓋了全球所有的文字和二進制的對應關系,
- Unicode 2-4位元組 已經收錄136690個字元,并還在一直不斷擴張中…
Unicode 起到了2個作用:
- 直接支援全球所有語言,每個國家都可以不用再使用自己之前的舊編碼了,用unicode就可以了。(就跟英語是全球統一語言一樣)
- unicode包含了跟全球所有國家編碼的映射關系。
Unicode解決了字元和二進制的對應關系,但是使用unicode表示一個字元,太浪費空間。例如:利用unicode表示“Python”需要12個位元組才能表示,比原來ASCII表示增加了1倍。
由于計算機的記憶體比較大,并且字元串在内容中表示時也不會特别大,是以内容可以使用unicode來處理,但是存儲和網絡傳輸時一般資料都會非常多,那麼增加1倍将是無法容忍的!!!
為了解決存儲和網絡傳輸的問題,出現了Unicode Transformation Format,學術名UTF,即:對unicode中的進行轉換,以便于在存儲和網絡傳輸時可以節省空間!
- UTF-8: 使用1、2、3、4個位元組表示所有字元;優先使用1個字元、無法滿足則使增加一個位元組,最多4個位元組。英文占1個位元組、歐洲語系占2個、東亞占3個,其它及特殊字元占4個
- UTF-16: 使用2、4個位元組表示所有字元;優先使用2個位元組,否則使用4個位元組表示
- UTF-32: 使用4個位元組表示所有字元;
總結:UTF 是為unicode編碼 設計 的一種 在存儲 和傳輸時節省空間的編碼方案。
字元在硬碟上的存儲
無論以什麼編碼在記憶體裡顯示字元,存到硬碟上都是2進制。
ascii編碼(美國):
l 0b1101100
o 0b1101111
v 0b1110110
e 0b1100101
GBK編碼(中國):
中 0b11000000 0b11001111
國 0b11000100 0b11010000
人 0b10111010 0b10100010
Shift_JIS編碼(日本):
私 0b10001110 0b10000100
は 0b10000010 0b11001101
ks_c_5601-編碼(南韓):
나 0b10110011 0b10101010
는 0b10110100 0b11000010
TIS-編碼(泰國):
ฉัน 0b10101001 0b11010001 0b10111001
...
####要注意的是,存到硬碟上時是以何種編碼存的,再從硬碟上讀出來時,就必須以何種編碼讀,要不然就亂了。。
編碼的轉換
雖然國際語言是英語 ,但大家在自己的國家依然說自已的語言,不過出了國, 你就得會英語
編碼也一樣,雖然有了unicode and utf-8 , 但是由于曆史問題,各個國家依然在大量使用自己的編碼,比如中國的windows,預設編碼依然是gbk,而不是utf-8
基于此,如果中國的軟體出口到美國,在美國人的電腦上就會顯示亂碼,因為他們沒有gbk編碼。
若想讓中國的軟體可以正常的在 美國人的電腦上顯示,隻有以下2條路可走:
- 讓美國人的電腦上都裝上gbk編碼
- 把你的軟體編碼以utf-8編碼
第1種方法幾乎不可能實作,第2種方法比較簡單。 但是也隻能是針對新開發的軟體。 如果你之前開發的軟體就是以gbk編碼的,上百萬行代碼可能已經寫出去了,重新編碼成utf-8格式也會費很大力氣。
so , 針對已經用gbk開發完畢的項目,以上2種方案都不能輕松的讓項目在美國人電腦上正常顯示,難道沒有别的辦法了麼?
有, 還記得我們講unicode其中一個功能是其包含了跟全球所有國家編碼的映射關系,意思就是,你寫的是gbk的“路飛學城”,但是unicode能自動知道它在unicode中的“路飛學城”的編碼是什麼,如果這樣的話,那是不是意味着,無論你以什麼編碼存儲的資料 ,隻要你的軟體在把資料從硬碟讀到記憶體裡,轉成unicode來顯示,就可以了。
由于所有的系統、程式設計語言都預設支援unicode,那你的gbk軟體放到美國電腦 上,加載到記憶體裡,變成了unicode,中文就可以正常展示啦。
unicode與gbk的映射表 http://www.unicode.org/charts/
![](https://img.laitimes.com/img/__Qf2AjLwojIjJCLyojI0JCLiAzNvwVZ2x2bzNXak9CX90TQNNkRrFlQKBTSvwFbslmZvwFMwQzLcVmepNHdu9mZvwFVywUNMZTY18CX052bm9CX9EFVNpXR6F2a10WYxBnMMBjVtJWd0ckW65UbM5WOHJWa5kHT20ESjBjUIF2LcRHelR3LcJzLctmch1mclRXY39zN4UzN0QzM4AjMwQDM4EDMy8CX0Vmbu4GZzNmLn9Gbi1yZtl2Lc9CX6MHc0RHaiojIsJye.jpg)
python3的執行過程
在看實際代碼的例子前,我們來聊聊,python3 執行代碼的過程
解釋器找到代碼檔案,把代碼字元串按檔案頭定義的編碼加載到記憶體,轉成unicode
把代碼字元串按照文法規則進行解釋,
所有的變量字元都會以unicode編碼聲明
編碼轉換過程
代碼示範如下,在py3上 把你的代碼以utf-8編寫, 儲存,然後在windows上執行,
s = '中國'
print(s)
so ,一切都很美好,到這裡,我們關于編碼的學習按說就可以結束了。
是,如生活一樣,美好的表面下,總是隐藏着不盡如人意,上面的utf-8編碼之是以能在windows gbk的終端下顯示正常,是因為到了記憶體裡python解釋器把utf-8轉成了unicode , 但是這隻是python3, 并不是所有的程式設計語言在記憶體裡預設編碼都是unicode,比如 萬惡的python2 就不是, 它的預設編碼是ASCII,想寫中文,就必須聲明檔案頭的coding為gbk or utf-8, 聲明之後,python2解釋器僅以檔案頭聲明的編碼去解釋你的代碼,加載到記憶體後,并不會主動幫你轉為unicode,也就是說,你的檔案編碼是utf-8,加載到記憶體裡,你的變量字元串就也是utf-8, 這意味着什麼你知道麼?。。。意味着,你以utf-8編碼的檔案,在windows是亂碼。
在python2中執行上述代碼:
再加上編碼聲明後如下代碼:
#_*_coding:utf8_*_
s = '中國'
print(s)
亂是正常的,不亂才不正常,因為隻有2種情況 ,你的windows上顯示才不會亂
字元串以GBK格式顯示
字元串是unicode編碼
既然Python2并不會自動的把檔案編碼轉為unicode存在記憶體裡, 那就隻能使出最後一招了,你自己人肉轉。Py3 自動把檔案編碼轉為unicode必定是調用了什麼方法,這個方法就是,decode(解碼) 和encode(編碼)
UTF-8 --> decode 解碼 --> Unicode
Unicode --> encode 編碼 --> GBK / UTF-8 ..
decode示例:
#_*_coding:utf8_*_
s = '中國'
print type(s)
print s
s2 = s.decode('utf8_')
print type(s2)
print s2
s3 = s2.encode('GBK')
print type(s3)
print s3
記住下圖規則:
Python bytes類型
在python 2 上寫字元串
>>> s = "中國"
>>> print s
中國
>>> s
'\xe8\xb7\xaf\xe9\xa3\x9e'
雖說列印的是中國,但直接調用變量s,看到的卻是一個個的16進制表示的二進制位元組,我們怎麼稱呼這樣的資料呢?直接叫二進制麼?也可以, 但相比于010101,這個資料串在表示形式上又把2進制轉成了16進制來表示,這是為什麼呢? 哈,為的就是讓人們看起來更可讀。我們稱之為bytes類型,即位元組類型, 它把8個二進制一組稱為一個byte,用16進制來表示。
說這個有什麼意思呢?
想告訴你一個事實, 就是,python2的字元串其實更應該稱為位元組串。 通過存儲方式就能看出來,但python2裡還有一個類型是bytes呀,難道又叫bytes又叫字元串? 嗯 ,是的,在python2裡,bytes == str , 其實就是一回事
除此之外呢, python2裡還有個單獨的類型是unicode , 把字元串解碼後,就會變成unicode
>>> s
'\xe8\xb7\xaf\xe9\xa3\x9e' #utf-8
>>> s.decode('utf-8')
u'\u8def\u98de' #unicode 在unicode編碼表裡對應的位置
>>> print(s.decode('utf-8'))
#unicode 格式的字元
由于Python創始人在開發初期認知的局限性,其并未預料到python能發展成一個全球流行的語言,導緻其開發初期并沒有把支援全球各國語言當做重要的事情來做,是以就輕佻的把ASCII當做了預設編碼。 當後來大家對支援漢字、日文、法語等語言的呼聲越來越高時,Python于是準備引入unicode,但若直接把預設編碼改成unicode的話是不現實的, 因為很多軟體就是基于之前的預設編碼ASCII開發的,編碼一換,那些軟體的編碼就都亂了。是以Python 2 就直接 搞了一個新的字元類型,就叫unicode類型,比如你想讓你的中文在全球所有電腦上正常顯示,在記憶體裡就得把字元串存成unicode類型
>>> s = "中國"
>>> s
'\xe8\xb7\xaf\xe9\xa3\x9e'
>>> s2 = s.decode("utf-8")
>>> s2
u'\u8def\u98de'
>>> type(s2)
<type 'unicode'>
時間來到2008年,python發展已近20年,創始人龜叔越來越覺得python裡的好多東西已發展的不像他的初衷那樣,開始變得臃腫、不簡潔、且有些設計讓人摸不到頭腦,比如unicode 與str類型,str 與bytes類型的關系,這給很多python程式員造成了困擾。
龜叔再也忍不了,像之前一樣的修修補補已不能讓Python變的更好,于是來了個大變革,Python3橫空出世,不相容python2,python3比python2做了非常多的改進,其中一個就是終于把字元串變成了unicode,檔案預設編碼變成了utf-8,這意味着,隻要用python3,無論你的程式是以哪種編碼開發的,都可以在全球各國電腦上正常顯示,真是太棒啦!
PY3 除了把字元串的編碼改成了unicode, 還把str 和bytes 做了明确區分, str 就是unicode格式的字元, bytes就是單純二進制啦。
最後一個問題,為什麼在py3裡,把unicode編碼後,字元串就變成了bytes格式? 你直接給我直接列印成gbk的字元展示不好麼?我想其實py3的設計真是煞費苦心,就是想通過這樣的方式明确的告訴你,想在py3裡看字元,必須得是unicode編碼,其它編碼一律按bytes格式展示。
最後再提示一下,Python隻要出現各種編碼問題,無非是哪裡的編碼設定出錯了
常見編碼錯誤的原因有:
- Python解釋器的預設編碼
- Python源檔案檔案編碼
- Terminal使用的編碼
- 作業系統的語言設定
掌握了編碼之前的關系後,挨個排錯就好啦