字符、字符集、編碼,以及它們python中會遇到的一些問題(下)
在看了很多的博客文章之後,總結整理得到了以下文章,非常感謝這些無私奉獻的博主!
文章末尾有本文引用的文章的鏈接,如果有漏掉的文章引用,可以發郵件聯系我,隨後再次附上鏈接!
侵刪!!!
這一部分是下篇,主要講的是編碼部分,以及在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模塊來檢測命令行的默認編碼(也就是系統的編碼)和設置命令行編碼:
- import locale
- #get coding type
- print locale.getdefaultlocale()
- (‘zh_CN‘, ‘cp936‘)
- #set coding type
- locale.setlocale(locale.LC_ALL, locale=‘zh_CN.GB2312‘)
- print locale.getlocale()
-
(‘zh_CN‘, ‘gb2312‘)
表明當前系統的內部編碼是cp936,近似於GBK。實際上中文XP和WIN7的系統內部編碼都是cp936(GBK)。
tips:linux系統下系統默認編碼為utf8編碼,window默認編碼為gbk編碼
python編碼:指python內設置的解碼方式。如果不設定的話,python默認的是ascii解碼方式。如果python源代碼文件中不出現中文的話,這個地方怎麽設定應該不會問題。
- #查看python編碼
- import sys
- sys.getdefaultencoding()
- #結果
- ‘ascii‘
- #更改python編碼
- sys.setdefaultencoding(‘utf-8‘)
永久地將python默認采用的編碼設置為utf-8:在python的Lib\site-packages文件夾下新建一個sitecustomize.py,內容為:
- # encoding=utf8
- import sys
- reload(sys)
- 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將先使用源代碼文件聲明的字符編碼進行編碼然後寫入。
- # coding: UTF-8
- f = open(‘test.txt‘)
- s = f.read()
- f.close()
- print type(s) # <type ‘str‘>
- # 已知是GBK編碼,解碼成unicode
- u = s.decode(‘GBK‘)
- f = open(‘test.txt‘, ‘w‘)
- # 編碼成UTF-8編碼的str
- s = u.encode(‘UTF-8‘)
- f.write(s)
-
f.close()
另外,模塊codecs提供了一個open()方法,可以指定一個編碼打開文件,使用這個方法打開的文件讀取返回的將是unicode。寫入時,如果參數是unicode,則使用open()時指定的編碼進行編碼後寫入;如果是str,則先根據源代碼文件聲明的字符編碼,解碼成unicode後再進行前述操作。相對內置的open()來說,這個方法比較不容易在編碼上出現問題。使用codecs直接開unicode通道。
- # coding: GBK
- import codecs
- f = codecs.open(‘test.txt‘, encoding=‘UTF-8‘)
- u = f.read()
- f.close()
- print type(u) # <type ‘unicode‘>
- f = codecs.open(‘test.txt‘, ‘a‘, encoding=‘UTF-8‘)
- # 寫入unicode
- f.write(u)
- # 寫入str,自動進行解碼編碼操作
- # GBK編碼的str
- s = ‘漢‘
- print repr(s) # ‘\xba\xba‘
- # 這裏會先將GBK編碼的str解碼為unicode再編碼為UTF-8寫入
- f.write(s)
- 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再轉一次碼。
- sys.stdout.encoding
- ‘cp936‘
- ‘嚴‘
- ‘\xe4\xb8\xa5‘
- print u‘嚴‘
- 嚴
- print u‘嚴‘.encode(‘utf-8‘)
- 涓
- print u‘嚴‘.encode(‘gbk‘)
- 嚴
- u‘嚴‘.encode(‘utf-8‘)
- ‘\xe4\xb8\xa5‘
- u‘涓‘.encode(‘gbk‘)
-
‘\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)
- #gbk就是源碼的編碼方式,解碼成了unicode
- ‘\xa1\xb0‘.decode(‘gbk‘) #解碼
- u‘\u201c‘ #unicode
encode:編碼(從unicode到其他方向(utf-8,gbk等.......))
- u‘中文‘.encode(‘utf-8‘)
- ‘\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,一開始就運行:
- import sys
- sys.stdout.encoding
- ‘utf-8‘
- sys.stdin.encoding
- ‘utf-8‘
- print ‘中文‘
- 中文
- ‘中文‘
-
‘\xe4\xb8\xad\xe6\x96\x87‘
完事之後,運行一個py(比如tb.py)文件,然後,神奇的事情發生了:
- sys.stdin.encoding
- ‘cp936‘
- sys.stdout.encoding
- ‘cp936‘
- print ‘中文‘
- 濂沖+
- ‘中文‘
-
‘\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中會遇到的一些問題(下)