文章目錄
-
- 背景
- 首先明确幾個概念:
- 針對python,先把結論放在前面,三點:
- 例子1:
-
- ---終端為UTF-8,locale為zh_CN.GBK-----------------
- ---終端為UTF-8,locale為zh_CN.UTF-8-----------------
- ---終端為GBK,locale為zh_CN.GBK-----------------
- ---終端為GBK,locale為zh_CN.UTF-8-----------------
- 例子1總結,對print而言:
- 例子2:
- 關于str()和repr()
- 關于終端和伺服器的編碼
- 參考資料
背景
多次被python的中文編碼/中文亂碼問題困擾,相信pythoner們都被困擾過,網上鋪天蓋地的資料太多也參差不齊,就整理了下。本文從使用的角度系統總結了python編碼相關的一些概念,将本文的例子玩一遍,基本上對python的編碼問題就清楚了。
首先明确幾個概念:
- 位元組流:以utf8/gbk等編碼編碼的位元組流。
- unicode對象:python代碼中,
, 或者a=u'中國'
的結果。a='中國'.decode()
- terminal用于顯示字元的編碼:将一個用utf8/gbk編碼的位元組流通過terminal指定的編碼,去查找對應的字元顯示出來。
- locale:linux下,Locale 是軟體在運作時的語言環境, 它包括語言(Language), 地域 (Territory) 和字元集(Codeset)。一個locale的書寫格式為: 語言[_地域[.字元集]]. 是以說呢,locale總是和一定的字元集相聯系的。比如:zh_CN.GB2312
- 編碼轉換原則:unicode是"中介",任何編碼之間轉換都需要先decode()到unicode。
針對python,先把結論放在前面,三點:
- #coding:utf-8 #.py檔案是什麼編碼就需要告訴python用什麼編碼去讀取這個.py檔案。
- sys.stdout.encoding,預設就是locale的編碼,print會用sys.stdout.encoding去encode()成位元組流,交給terminal顯示。是以locale需要與terminal一緻,才能正确print列印出中文。
- sys.setdefaultencoding(‘utf8’),用于指定str.encode() str.decode()的預設編碼,預設是ascii。
- 對編碼字元串a,代碼中可以直接寫a.encode(“gbk”),但事實上内部自動先通過defaultencoding 去decode成unicode之後再encode()的。
- str(xxx)應該也是用這個去編碼的。
-
print的時候出現這個錯誤一般可以使用這個方案去處理。'ascii' codec can't encode characters in position 7-8: ordinal not in range(128)
- 為了避免代碼中到處都要去encode(“xxx”),還有可能不同的地方寫得不一樣帶來不一緻的情況,推薦使用這個:
import sys
reload(sys)
sys.setdefaultencoding('utf8')
例子1:
- 在python中,unicode vs 位元組流:位元組流可以從unicode encode得到,unicode可以從utf8/gbk等編碼的位元組流decode得到。
- 分析下面這段代碼,終端/locale分别為不同編碼的情況:
#coding:utf-8 #由于.py檔案是utf-8的,是以必須有這一句
import sys
import locale
import os
import codecs
reload(sys)
print sys.getdefaultencoding() + " - sys.getdefaultencoding()"
sys.setdefaultencoding('utf8') #影響encode()
print sys.getdefaultencoding() + " - sys.getdefaultencoding()"
print sys.stdout.encoding + " - sys.stdout.encoding:"
#sys.stdout = codecs.getwriter('utf8')(sys.stdout) #影響print
print sys.stdout.encoding + " - sys.stdout.encoding:"
u = u'中國'
print u + " - u"
a = '中國'
print a + " - a"
print a.decode('utf-8') + " - a.decode('utf-8')"
print a.decode('utf-8').encode('gbk') + " - a.decode('utf-8').encode('gbk')"
print a.decode('utf-8').encode('utf-8') + " - a.decode('utf-8').encode('utf-8')"
print a.decode('utf-8').encode() + " - a.decode('utf-8').encode()"
print (sys.stdout.encoding) + " - (sys.stdout.encoding)"
print (sys.stdout.isatty())
print (locale.getpreferredencoding())
print (sys.getfilesystemencoding())
—終端為UTF-8,locale為zh_CN.GBK-----------------
ascii - sys.getdefaultencoding()
utf8 - sys.getdefaultencoding()
GBK - sys.stdout.encoding:
GBK - sys.stdout.encoding:
�й� - u
中國 - a
�й� - a.decode('utf-8')
�й� - a.decode('utf-8').encode('gbk')
中國 - a.decode('utf-8').encode('utf-8')
中國 - a.decode('utf-8').encode()
GBK - (sys.stdout.encoding)
True
GBK
utf-8
—終端為UTF-8,locale為zh_CN.UTF-8-----------------
ascii - sys.getdefaultencoding()
utf8 - sys.getdefaultencoding()
UTF-8 - sys.stdout.encoding:
UTF-8 - sys.stdout.encoding:
中國 - u
中國 - a
中國 - a.decode('utf-8')
�й� - a.decode('utf-8').encode('gbk')
中國 - a.decode('utf-8').encode('utf-8')
中國 - a.decode('utf-8').encode()
UTF-8 - (sys.stdout.encoding)
True
UTF-8
utf-8
—終端為GBK,locale為zh_CN.GBK-----------------
ascii - sys.getdefaultencoding()
utf8 - sys.getdefaultencoding()
GBK - sys.stdout.encoding:
GBK - sys.stdout.encoding:
中國 - u
涓???? - a
中國 - a.decode('utf-8')
中國 - a.decode('utf-8').encode('gbk')
涓???? - a.decode('utf-8').encode('utf-8')
涓???? - a.decode('utf-8').encode()
GBK - (sys.stdout.encoding)
True
GBK
utf-8
—終端為GBK,locale為zh_CN.UTF-8-----------------
ascii - sys.getdefaultencoding()
utf8 - sys.getdefaultencoding()
UTF-8 - sys.stdout.encoding:
UTF-8 - sys.stdout.encoding:
涓???? - u
涓???? - a
涓???? - a.decode('utf-8')
中國 - a.decode('utf-8').encode('gbk')
涓???? - a.decode('utf-8').encode('utf-8')
涓???? - a.decode('utf-8').encode()
UTF-8 - (sys.stdout.encoding)
True
UTF-8
utf-8
例子1總結,對print而言:
- unicode的資料如果要顯示正常,必須終端與locale一緻。sys.stdout.encoding這個值應該來自locale,print會以sys.stdout.encoding去encode并輸出到位元組流。
-
encode為終端編碼的位元組流就能顯示正常,無論locale是啥。
最終是terminal通過terminal配置的編碼規則去解碼成對應的字元并顯示出來。
例子2:
關于sys.setdefaultencoding(‘utf8’)的例子:
#coding:utf-8
import sys
reload(sys)
sys.setdefaultencoding('utf8')
print sys.getdefaultencoding() + " - sys.getdefaultencoding()"
a = '中國'
print a + " - a"
print a.encode("gbk") #并不是直接從utf8的位元組流轉化到gbk的,而是通過defaultencoding decode之後才轉的。
print a.decode() #使用預設的defaultencoding
print a.encode() #使用預設的defaultencoding
關于str()和repr()
- str()是對各種類型轉化成str,如果本來是encoded字元串,則不變,如果為unicode,會encode()
-
repr()對字元串是将位元組流出二進制的值以16進制轉化為可見字元。
測試環境locale為GBK
#coding:utf-8
import sys
reload(sys)
sys.setdefaultencoding("utf-8")
a = u'中國'
print a
print str(a)
print repr(a)
print repr(a.encode("utf-8"))
print repr(a.encode("gbk"))
中國
涓????
u'\u4e2d\u56fd'
'\xe4\xb8\xad\xe5\x9b\xbd'
'\xd6\xd0\xb9\xfa'
再深挖下去,還有repr()和eval()的關系,就不深挖了。
關于終端和伺服器的編碼
另外補充一些關于終端和伺服器編碼的結論:
- 對mac iterm2,如果server的locale與mac本地終端的locale一緻,才能保證server端與本地的表現一緻。
- cat a.py #就把檔案顯示出來,就是給terminal一串位元組流。terminal根據設定的終端編碼規則來顯示字元。是以隻要檔案編碼與terminal一緻即可,與locale無關。
- cat a.txt > b.txt #無論locale怎麼樣,隻跟a.txt原來的編碼相關
- echo “中國年過” > a.txt #這個情況下,隻有terminal與locale的編碼一緻,你才能在終端shell打出正确的中文~~~是以a.txt與兩者都會一緻
參考資料
關于vim:http://blog.chinaunix.net/uid-21843387-id-106001.html