unicode 與 utf-8 的關係
unicode 是 character set
- character set 是把每個字元對應成數字的集合,比如unicode中 A對應0041,漢字『我』對應 ‘6211’
- unicode 是個很大的集合,幾乎覆蓋世界上所有的字元,現在的規模已經可以容納100萬個字元。
utf-8 是對 unicode 儲存的實現方式
unicode 只定義字元對應的數字,但沒有規定這些數字如何儲存起來,比如像中文的『我』字儲存時需要兩個位元組來表示,而英文字母A卻只需要一個位元組,有些其他的字元可能需要3-4個位元組。
- 如果統一規定每個字元用3個或者4個位元組來儲存,那麼每個英文字元都必然需要額外2到3個0,這對儲存是很大的浪費。
- 如果每個字元按照實際需要的位元組數來儲存,計算機就分不清三個位元組是表示三個字元還是一個字元。
utf-8 是對 unicode 編碼儲存的一種實現方式,同樣的還有 utf-16, utf-32。
utf-8 是使用最廣泛的編碼方式,採用變長的編碼方式,可以使用1-4個位元組來表示一個字元; utf-16 用2個或4個位元組,utf-32 用4個位元組表示。編碼規則如下:
- 對於單位元組的符號,位元組的第一位設為0,後面7位為這個符號的unicode碼。因此對於英語字母, UTF-8編碼和ASCII碼是相同的。
- 對於n位元組的符號(n>1),第一個位元組的前n位都設為1,第n+1位設為0,後面位元組的前兩位一律設為10。剩下的沒有提及的二進位制位,全部為這個符號的unicode碼。
python2 中的 str 和 unicode
python2 中有字串型別有兩種:byte string (str)
和 unicode string (unicode)
。
1 2 3 4 5 6 7 8 9 |
>>> s = '美的' >>> s '\xe7\xbe\x8e\xe7\x9a\x84' >>> s = u'美的' >>> s u'\u7f8e\u7684' >>> s = '美的' >>> s.decode('utf-8') u'\u7f8e\u7684' |
上面的輸出中,第一個s的型別是 str,列印出來的內容是 utf-8 編碼過的內容。第二個s的型別是 unicode,列印出來的兩個雙位元組的數字分別表示了兩個漢字『美的』。
encode
和decode
提供 str 和 unicode 這兩種的型別的互相轉化。
- encode 把 unicode 轉化成 str(byte string)
- decode 把 str(byte string) 轉化成 unicode
本質上,str是存放的位元組序,有可能是 ascii, gbk, utf-8 等等中的任意一種,通過呼叫 decode 可以把他們轉化成 unicode ,預設的 decode 編碼是 ascii 。str中到底是用的哪一種編碼,取決於它所在的場景,跟 locale ,檔案編碼等等都有關係。
文字檔案、編輯器的處理
1 2 3 4 5 6 |
#!/usr/bin/env python # -*- coding: GBK -*- s = u'中文' print repr(s) print repr(s.encode('GBK')) |
比如上面的檔案enc.py
,儲存的時候選擇檔案編碼
是GBK,程式檔案本質上也是檔案,當我們使用某個外部的應用 開啟它時(編輯器或者python直譯器等),外部應用是不知道該檔案的編碼格式的,
這個時候有三種情況:
- 應用使用其預設的編碼方式去解析,比如UTF-8或者ASCII;python直譯器預設是ASCII,編輯器可以自己設定;
- 應用根據檔案中的位元組內容,自動檢測編碼方式;
- 文字檔案告訴應用使用什麼編碼方式去解碼;比如
# -*- coding: GBK -*-
告知直譯器使用GBK來解碼;
試驗一下,把# -*- coding: GBK -*-
刪除後,執行python enc.py
,輸出:
1 2 |
File "enc.py", line 4 SyntaxError: Non-ASCII character '\xd6' in file enc.py on line 4, but no encoding declared; |
試著用vim開啟該檔案時,『中文』兩個字就會顯示成亂碼,因為vim預設的檔案編碼方式被設定成UTF-8了。
1 2 3 4 5 6 7 8 9 10 11 12 |
#!/usr/bin/env python # -*- coding: GBK -*- s1 = u'中文' print repr(s1) print repr(s1.encode('GBK')) s2 = '中文' print repr(s2) print repr(s2.decode('GBK')) |
輸出結果:
1 2 3 4 |
u'\u4e2d\u6587' '\xd6\xd0\xce\xc4' '\xd6\xd0\xce\xc4' u'\u4e2d\u6587' |
從這裡可以看出來, s2中存放的是byte格式的從檔案中讀到的GBK編碼的內容。
再看下面的這段程式碼,程式檔案utf8_enc.py
,儲存成UTF-8編碼的。
1 2 3 4 5 6 7 8 9 10 11 12 |
#!/usr/bin/env python # -*- coding: utf-8 -*- s1 = u'中文' print repr(s1) print repr(s1.encode('GBK')) s2 = '中文' print repr(s2) print repr(s2.decode('GBK')) |
輸出:
1 2 3 4 5 6 7 |
u'\u4e2d\u6587' '\xd6\xd0\xce\xc4' '\xe4\xb8\xad\xe6\x96\x87' Traceback (most recent call last): File "unicode_enc.py", line 12, in <module> print repr(s2.decode('GBK')) UnicodeDecodeError: 'gbk' codec can't decode bytes in position 2-3: illegal multibyte sequence |
這裡同樣可以知道,s2中存放的是檔案儲存的編碼UTF-8的byte碼。
References
http://www.rrn.dk/the-differe…
http://www.ruanyifeng.com/blo…
https://docs.python.org/2/how…
http://yergler.net/2012/bytes…