天天看點

python中文編碼中文亂碼問題

文章目錄

    • 背景
    • 首先明确幾個概念:
    • 針對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的編碼問題就清楚了。

首先明确幾個概念:

  1. 位元組流:以utf8/gbk等編碼編碼的位元組流。
  2. unicode對象:python代碼中,

    a=u'中國'

    , 或者

    a='中國'.decode()

    的結果。
  3. terminal用于顯示字元的編碼:将一個用utf8/gbk編碼的位元組流通過terminal指定的編碼,去查找對應的字元顯示出來。
  4. locale:linux下,Locale 是軟體在運作時的語言環境, 它包括語言(Language), 地域 (Territory) 和字元集(Codeset)。一個locale的書寫格式為: 語言[_地域[.字元集]]. 是以說呢,locale總是和一定的字元集相聯系的。比如:zh_CN.GB2312
  5. 編碼轉換原則:unicode是"中介",任何編碼之間轉換都需要先decode()到unicode。

針對python,先把結論放在前面,三點:

  1. #coding:utf-8 #.py檔案是什麼編碼就需要告訴python用什麼編碼去讀取這個.py檔案。
  2. sys.stdout.encoding,預設就是locale的編碼,print會用sys.stdout.encoding去encode()成位元組流,交給terminal顯示。是以locale需要與terminal一緻,才能正确print列印出中文。
  3. sys.setdefaultencoding(‘utf8’),用于指定str.encode() str.decode()的預設編碼,預設是ascii。
  • 對編碼字元串a,代碼中可以直接寫a.encode(“gbk”),但事實上内部自動先通過defaultencoding 去decode成unicode之後再encode()的。
  • str(xxx)應該也是用這個去編碼的。
  • 'ascii' codec can't encode characters in position 7-8: ordinal not in range(128)

    print的時候出現這個錯誤一般可以使用這個方案去處理。
  • 為了避免代碼中到處都要去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()的關系,就不深挖了。

關于終端和伺服器的編碼

另外補充一些關于終端和伺服器編碼的結論:

  1. 對mac iterm2,如果server的locale與mac本地終端的locale一緻,才能保證server端與本地的表現一緻。
  2. cat a.py #就把檔案顯示出來,就是給terminal一串位元組流。terminal根據設定的終端編碼規則來顯示字元。是以隻要檔案編碼與terminal一緻即可,與locale無關。
  3. cat a.txt > b.txt #無論locale怎麼樣,隻跟a.txt原來的編碼相關
  4. echo “中國年過” > a.txt #這個情況下,隻有terminal與locale的編碼一緻,你才能在終端shell打出正确的中文~~~是以a.txt與兩者都會一緻

參考資料

關于vim:http://blog.chinaunix.net/uid-21843387-id-106001.html