1. 程式人生 > >字符、字符集、編碼,以及它們python中會遇到的一些問題(下)

字符、字符集、編碼,以及它們python中會遇到的一些問題(下)

區別 做了 and 內部 eve nbsp nes 文字 相對

在看了很多的博客文章之後,總結整理得到了以下文章,非常感謝這些無私奉獻的博主!

文章末尾有本文引用的文章的鏈接,如果有漏掉的文章引用,可以發郵件聯系我,隨後再次附上鏈接!

侵刪!!!

這一部分是下篇,主要講的是編碼部分,以及在python中會遇到的一些編碼問題,偏向於實際應用一點。

上篇介紹了字符、字符集的一些概念,以及他們在python中的一些簡單的代碼示例,偏向於概念。

上篇地址:http://www.cnblogs.com/echo-coding/p/7435118.html

這絕對是個源遠流長的大坑,對於新手來說惡心致死(尤其是windows)...........

二、decode、encode(python編碼)

上面介紹了字符、字符集和字符編碼,為這一小結做了準備。

一些重要概念:

系統編碼:默認編碼,正常情況下window系統默認是gbk,Linux系統默認是utf-8,可用locale.getdefaultlocale()和locale.setdefaultlocale()來控制,與encode有關

用python自帶的locale模塊來檢測命令行的默認編碼(也就是系統的編碼)和設置命令行編碼:

  1. import locale
  2. #get coding type
  3. print locale.getdefaultlocale()
  4. (‘zh_CN‘, ‘cp936‘)
  5. #set coding type
  6. locale.setlocale(locale.LC_ALL, locale=‘zh_CN.GB2312‘)
  7. print locale.getlocale()
  8. (‘zh_CN‘, ‘gb2312‘)

表明當前系統的內部編碼是cp936,近似於GBK。實際上中文XP和WIN7的系統內部編碼都是cp936(GBK)。

tips:linux系統下系統默認編碼為utf8編碼,window默認編碼為gbk編碼

python編碼:指python內設置的解碼方式。如果不設定的話,python默認的是ascii解碼方式。如果python源代碼文件中不出現中文的話,這個地方怎麽設定應該不會問題。

  1. #查看python編碼
  2. import sys
  3. sys.getdefaultencoding()
  4. #結果
  5. ‘ascii‘
  6. #更改python編碼
  7. sys.setdefaultencoding(‘utf-8‘)

永久地將python默認采用的編碼設置為utf-8:在python的Lib\site-packages文件夾下新建一個sitecustomize.py,內容為:

  1. # encoding=utf8
  2. import sys
  3. reload(sys)
  4. sys.setdefaultencoding(‘utf8‘)

此時重啟python解釋器,執行sys.getdefaultencoding(),發現編碼已經被設置為utf8的了,多次重啟之後,效果相同,這是因為系統在python啟動的時候,自行調用該文件,設置系統的默認編碼,而不需要每次都手動的加上解決代碼,屬於一勞永逸的解決方法。

文件編碼:文本的編碼方式,sys.getfilesystemencoding()

讀寫文件:

內置的open()方法打開文件時,read()讀取的是str,讀取後需要使用正確的編碼格式進行decode()(變成unicode)。write()寫入時,如果參數是unicode,則需要使用你希望寫入的編碼進行encode(),如果是其他編碼格式的str,則需要先用該str的編碼進行decode(), 轉成unicode後再使用寫入的編碼進行encode()。如果直接將unicode作為參數傳入write()方法,Python將先使用源代碼文件聲明的字符編碼進行編碼然後寫入。

  1. # coding: UTF-8
  2. f = open(‘test.txt‘)
  3. s = f.read()
  4. f.close()
  5. print type(s) # <type ‘str‘>
  6. # 已知是GBK編碼,解碼成unicode
  7. u = s.decode(‘GBK‘)
  8. f = open(‘test.txt‘, ‘w‘)
  9. # 編碼成UTF-8編碼的str
  10. s = u.encode(‘UTF-8‘)
  11. f.write(s)
  12. f.close()

另外,模塊codecs提供了一個open()方法,可以指定一個編碼打開文件,使用這個方法打開的文件讀取返回的將是unicode。寫入時,如果參數是unicode,則使用open()時指定的編碼進行編碼後寫入;如果是str,則先根據源代碼文件聲明的字符編碼,解碼成unicode後再進行前述操作。相對內置的open()來說,這個方法比較不容易在編碼上出現問題。使用codecs直接開unicode通道。

  1. # coding: GBK
  2. import codecs
  3. f = codecs.open(‘test.txt‘, encoding=‘UTF-8‘)
  4. u = f.read()
  5. f.close()
  6. print type(u) # <type ‘unicode‘>
  7. f = codecs.open(‘test.txt‘, ‘a‘, encoding=‘UTF-8‘)
  8. # 寫入unicode
  9. f.write(u)
  10. # 寫入str,自動進行解碼編碼操作
  11. # GBK編碼的str
  12. s =
  13. print repr(s) # ‘\xba\xba‘
  14. # 這裏會先將GBK編碼的str解碼為unicode再編碼為UTF-8寫入
  15. f.write(s)
  16. f.close()

python代碼中的編碼(代碼編碼):

1、python代碼中的字符串在未被指定編碼的情況下,默認編碼與代碼文件本身的編碼一致。舉個例子:str = ‘中文‘這個字符串,如果是在utf8編碼的代碼文件中,該字符串就是utf8編碼;如果是在gb2312的文件中,該字符串就是gb2312編碼。那麽代碼文件本身的編碼怎麽知道呢?

(1)自己指定代碼文件的編碼:在代碼文件的頭部加上"#-*- coding:utf-8 -*-"來聲明該代碼文件為utf-8編碼。此時未被指定編碼的字符串的編碼都變成了utf-8。

頂部的:# -*- coding: utf-8 -*-目前看來有三個作用。

1、如果代碼中有中文註釋,就需要此聲明(否則代碼報錯,無法解析)

2、比較高級的編輯器(比如我的emacs),會根據頭部聲明,將此作為代碼文件的格式。

3、程序會通過頭部聲明,解碼初始化 u"人生苦短",這樣的unicode對象,(所以頭部聲明和代碼的存儲格式要一致)

(2)在沒有指定代碼文件的編碼時,創建代碼文件時使用的是python默認采用的編碼(一般來說是ascii碼,在windows中實際保存為cp936(GBK)編碼)。通過sys.getdefaultencoding()和sys.setdefaultencoding(‘...‘)來獲取和設置該默認編碼。

終端輸入輸出編碼:sys.stdin.encoding,sys.stdout.encoding,必須與locale編碼保持一致,才能print出正確str。

print會根據sys.stdout.encoding再轉一次碼

  1. sys.stdout.encoding
  2. ‘cp936‘
  3. ‘嚴‘
  4. ‘\xe4\xb8\xa5‘
  5. print u‘嚴‘
  6. print u‘嚴‘.encode(‘utf-8‘)
  7. print u‘嚴‘.encode(‘gbk‘)
  8. u‘嚴‘.encode(‘utf-8‘)
  9. ‘\xe4\xb8\xa5‘
  10. u‘涓‘.encode(‘gbk‘)
  11. ‘\xe4\xb8‘

print打印顯示的過程

Python2.7中調用print打印var 變量時,操作系統會對var做一定的字符處理:如果var是str類型的變量,則直接將var變量交付給終端進行顯示;如果var變量是unicode類型,則操作系統首先將var編碼成str類型的對象(編碼格式取決於stdout的編碼格式),然後再交由終端進行顯示。在終端顯示時,如果str類型的變量的編碼方式和終端設置的編碼方式不一致,很可能會出現亂碼問題。

還有就是print字符串的時候出現的編碼錯誤問題。原因在於sys.stdout.encoding。print 後面跟的字符串對象的編碼類型一定要與sys.stdout.encoding所指定的一致,不一致就會出現編碼錯誤。

console不能正常顯示中文,console的編碼是由操作系統決定的(windows環境下);

我的操作系統是win8中文版(GBK)

console的編碼決定了sys.stdout.encoding的取值,sys.stdout.encoding=‘cp936‘

decode && encode :

decode:解碼(從其他方向(utf-8,gbk等.......)到unicode)

  1. #gbk就是源碼的編碼方式,解碼成了unicode
  2. ‘\xa1\xb0‘.decode(‘gbk‘) #解碼
  3. u‘\u201c‘ #unicode

encode:編碼(從unicode到其他方向(utf-8,gbk等.......))

  1. u中文.encode(‘utf-8‘)
  2. ‘\xe4\xb8\xad\xe6\x96\x87‘

簡單來說就是,你用的什麽規則去編碼,你就得用這個規則去解碼,否則,要不然就亂碼了,要不然就直接報錯,沒法解啊!!!

但問題就是呢,系統有系統的默認編碼格式,你文件明明是utf-8的編碼,他偏偏用gbk方式去解碼,那要不然就直接解不了,要不然就是牛頭不對馬嘴唄。

關於打印:

你在打印str的時候,實際就是直接將字節流發送給shell。如果你的字節流編碼格式與shell的編碼格式不相同,就會亂碼。

而你在打印unicode的時候,系統自動將其編碼為shell的編碼格式,是不會出現亂碼的。

其他命令:

文件系統的編碼:sys.getfilesystemencoding()

終端的輸入編碼:sys.stdin.encoding

終端的輸出編碼:sys.stdout.encoding

一些建議:

1. 使用字符編碼聲明,並且同一工程中的所有源代碼文件使用相同的字符編碼聲明;

2. 拋棄str,全部使用unicode:按引號前先按一下u,這麽做可以減少90%的編碼問題;

3. 使用codecs.open()替代內置的open();

4. 絕對需要避免使用的字符編碼:MBCS/DBCS和UTF-16;

5、主動設置defaultencoding。(默認的是ascii);

6、代碼文件的保存格式要與文件頭部的# coding:xxx一致。

其他:

python 3和2很大區別就是python本身改為默認用unicode編碼,字符串不再區分"abc"和u"abc", 字符串"abc"默認就是unicode,不再代表本地編碼。

python2.7以後不用setdefaultencoding了,這兩個(聲明頭部和setdefaultencoding)是沒有區別的

這兩個作用不一樣:

1. # coding:utf-8 作用是定義源代碼的編碼。如果沒有定義, 此源碼中是不可以包含中文字符串的;

2. sys.getdefaultencoding() 是設置默認的string的編碼格式。

問題的根源:Python2 中的 string

Python 為了讓其語法看上去簡潔好用,做了很多 tricky 的事情,混淆 byte string 和 text string 就是其中一例。

在 Python 裏,有三大類 string 類型,unicode(text string),str(byte string,二進制數據),basestring,是前兩者的父類。

其實,在語言設計領域,一串字節(sequences of bytes)是否應該當做字符串(string)一直是存在爭議的。我們熟知的 Java 和 C# 投了反對票,而 Python 則站在了支持者的陣營裏。其實我們在很多情況下,給文本做的操作,比如正則匹配、字符替換等,對於字節來說是用不著的。而 Python 認為字節就是字符,所以他們倆的操作集合是一致的。

然後進一步的,Python 會在必要的情況下,嘗試對字節做自動類型轉換,例如,在上文中的 ==,或者字節和文本拼接時。如果沒有一個編碼(encoding),兩個不同類型之間的轉換是無法進行的,於是,Python 需要一個默認編碼。在 Python2 誕生的年代,ASCII 是最流行的(可以這麽說吧),於是 Python2 選擇了 ASCII。然而,眾所周知,在需要需要轉換的場景,ASCII 都是沒用的(128個字符,夠什麽吃)。

在歷經這麽多年吐槽後,Python 3 終於學乖了。默認編碼是 Unicode,這也就意味著,做所有需要轉換的場合,都能正確並成功的轉換。

一個非常困惑不解的地方:

打開ipython,一開始就運行:

  1. import sys
  2. sys.stdout.encoding
  3. ‘utf-8‘
  4. sys.stdin.encoding
  5. ‘utf-8‘
  6. print 中文
  7. 中文
  8. 中文
  9. ‘\xe4\xb8\xad\xe6\x96\x87‘

完事之後,運行一個py(比如tb.py)文件,然後,神奇的事情發生了:

  1. sys.stdin.encoding
  2. ‘cp936‘
  3. sys.stdout.encoding
  4. ‘cp936‘
  5. print 中文
  6. 濂沖+
  7. 中文
  8. ‘\xe4\xb8\xad\xe6\x96\x87‘

至今無解。。。。。。

參考資料和博客:

http://www.ruanyifeng.com/blog/2007/10/ascii_unicode_and_utf-8.html

http://blog.chinaunix.net/uid-200142-id-4461708.html

http://www.cnblogs.com/evening/archive/2012/04/19/2457440.html

http://blog.csdn.net/olanlanxiari/article/details/48201231

http://www.jb51.net/article/87739.htm

http://www.cnblogs.com/JohnABC/p/4015504.html

https://m.baidu.com/[email protected]_2001/from=0/bd_page_type=1/ssid=0/uid=0/pu=sz%401321_2001%2Cta%40utouch_1_10.2_3_602/baiduid=F9234C37D7B29B954D4706B9B57F904E/w=0_10_unicode+gbk%E7%BC%96%E7%A0%81/t=wap/l=3/tc?ref=www_utouch&lid=13436452897767398859&order=3&vit=osres&tj=www_normal_3_0_10_title&m=8&srd=1&dict=20&title=%E6%B7%B1%E5%85%A5%E7%90%86%E8%A7%A3-%E5%AD%97%E7%AC%A6%E7%BC%96%E7%A0%81ASCII%2CGB2312%2CGBK%2CUnicode%2CUTF-8-...&sec=21874&di=38f964cdb7b434e5&bdenc=1&nsrc=IlPT2AEptyoA_yixCFOxXnANedT62v3IJBaOMmBXATq5953ybrWxBcNpZCPcMWmTHUOwdoS4ctlMcyju3mRUjMU4f4MscH9hbXjcbPXwehGB

https://m.baidu.com/[email protected]_2001/from=0/bd_page_type=1/ssid=0/uid=0/pu=sz%401321_2001%2Cta%40utouch_1_10.2_3_602/baiduid=F9234C37D7B29B954D4706B9B57F904E/w=0_10_unicode+gbk%E7%BC%96%E7%A0%81/t=wap/l=3/tc?ref=www_utouch&lid=13436452897767398859&order=1&vit=osres&tj=www_normal_1_0_10_title&m=8&srd=1&dict=30&title=ASCIIUnicodeGBK%E5%92%8CUTF-8%E5%AD%97%E7%AC%A6%E7%BC%96%E7%A0%81%E7%9A%84%E5%8C%BA%E5%88%AB%E8%81%94..._%E5%8D%9A%E5%AE%A2%E5%9B%AD&sec=21874&di=265cf5a20e05fae9&bdenc=1&nsrc=IlPT2AEptyoA_yixCFOxXnANedT62v3IEQGG_ytK1DK6mlrte4viZQRASDfaR8qMH6DrvWz0sqdFtXLR_mUp7BJ2qrIwdzZz

http://www.cnblogs.com/work115/p/5924446.html

https://blog.ernest.me/post/python-setdefaultencoding-unicode-bytes

字符、字符集、編碼,以及它們python中會遇到的一些問題(下)