注意:本文只是針對 python 2,在 python 3 中,編碼方式與處理技巧有些許變化,具體請參考:php
Python 2 與 Python 3 的差別對比: http://my.oschina.net/leejun2005/blog/173553 html
在python源碼中若是使用了中文字符,運行時會有錯誤,解決的辦法是在源碼的開頭部分加入字符編碼的聲明,下面是一個例子:
#!/usr/bin/env python
# -*- coding: cp936 -*-
Python Tutorial中指出,python的源文件能夠編碼ASCII之外的字符集,最好的作法是在#!行後面用一個特殊的註釋行來定義字符集:
# -*- coding: encoding -*-
根據這個聲明,Python會嘗試將文件中的字符編碼轉爲encoding編碼,而且,它儘量的將指定地編碼直接寫成Unicode文本。
注意,coding:encoding只是告訴Python文件使用了encoding格式的編碼,可是編輯器可能會以本身的方式存儲.py文件,所以最後文件保存的時候還須要編碼中選指定的ecoding才行。python
>>> str = u"中文"
>>> str
u'\xd6\xd0\xce\xc4'
>>> str = "中文"
>>> str
'\xd6\xd0\xce\xc4'
u"中文"只是聲明unicode,實際的編碼並無變。這樣子就發生變化了:
>>> str = "中文"
>>> str
'\xd6\xd0\xce\xc4'
>>> str = str.decode("gb2312")
>>> str
u'\u4e2d\u6587'
更進一步:
>>> s = '中文'
>>> s.decode('gb2312')
u'\u4e2d\u6587'
>>> len(s)
4
>>> len(s.decode('gb2312'))
2
>>> s = u'中文'
>>> len(s)
4
>>> s = '中文test'
>>> len(s)
8
>>> len(s.decode('gb2312'))
6
>>> s = '中文test,'
>>> len(s)
10
>>> len(s.decode('gb2312'))
7
能夠看出,對於實際Non-ASCII編碼存儲的字符串,python能夠正確的識別出其中的中文字符以及中文上下文中的標點符號。
前綴「u」表示「後面這個字符串「是一個Unicode字符串」,這僅僅是一個聲明,並不表示這個字符串就真的是Unicode了;就比如某正太聲稱本身已滿18歲,但實際上他的真實年齡並不肯定,如今體育界年齡造假可不稀罕幺!
那麼聲明成u有什麼做用呢?對於Python來講,只要你聲明某字符串是Unicode,它就會用Unicode的一套機制對它進行處理。比方說,作字符串操做的時候會動用到內部的Unicode處理函數,保存的時候以Unicode字符(雙字節)進行保存。等等。顯而易見,對於一個實際上並非Unicode的字符串,作Unicode動做的處理,是有可能會出問題的。 u前綴只適用於你的字符串常量真的是Unicode的狀況 。git
用python處理字符串很容易,可是在處理中文的時候須要注意一些問題。好比:
a = "咱們是python愛好者"
print a[0]
只能輸出「我」字的前半部分,要想輸出整個的「我」字還須要:
b = a[0:2]
print b
才行,很不方便,而且當一段文本中同時有中英文如何處理?最好的辦法就是轉換爲unicode。像這樣:
c = unicode(a, "gb2312")
print c[0]
這個時候c的下標對應的就是每個字符,再也不是字節,而且經過len(c)就能夠得到字符數!還能夠很方便的轉換爲其餘編碼,好比轉換爲utf-8:github
d = c.encode("utf-8") segmentfault
<type ‘str’>將字符串看做是字節的序列,而<type ‘unicode’>則將其看做是字符的序列,單個字符可能佔用多個字節;字節相對於字符,其在存儲層次中更低一些。數組
str轉換爲unicode要decode,能夠這樣想,由於要把字節序列解釋成字符序列,字節序列是底層的存放方式,解碼(decode)成更高層的字符以便使用;同理,unicode轉換爲str要encode,就象信息編碼(encode)後才存儲同樣:
s.decode(encoding) <type 'str'> to <type 'unicode'>
u.encode(encoding) <type 'unicode'> to <type 'str'>
例如:
>>> s = 'str'
>>> type(s)
<type 'str'>
>>> type(s.decode())
<type 'unicode'>
>>> s = u'str'
>>> type(s)
<type 'unicode'>
>>> type(s.encode())
<type 'str'>
處理中文數據時最好採用以下方式:
1. Decode early(儘早decode, 將文件中的內容轉化成unicode再進行下一步處理)
2. Unicode everywhere (程序內部處理都用unicode)
3. Encode late (最後encode回所需的encoding, 例如把最終結果寫進結果文件)
下面是一個簡單的演示,用re庫查詢一箇中文字符串並打印:
>>> p = re.compile(unicode("測試(.*)", "gb2312"))
>>> s = unicode("測試一二三", "gb2312")
>>> for i in p.findall(s):
print i.encode("gb2312")編輯器
一二三函數
解決這一類的問題最好的方法就是在程序開頭加上如下幾行代碼:測試
#coding:utf-8 import sys reload(sys) sys.setdefaultencoding("utf-8")
那麼就可助你解決幾乎95%的這種問題, 可是若是想刨根問底的話, 就須要去了解不少東西了.
首先, 這個就是Python語言自己的問題. 由於在Python 2.x的語法中, 默認的str並非真正意義上咱們理解的字符串, 而是一個byte數組, 或者能夠理解成一個純ascii碼字符組成的字符串, 與Python 3中的bytes類型的變量對應; 而真正意義上通用的字符串則是unicode類型的變量, 它則與Python 3中的str變量對應. 原本應該用做byte數組的類型, 卻被用來作字符串用, 這種看似奇葩的設定是Python 2一直被人詬病的東西, 不過也沒有辦法, 爲了與以前的程序保持兼容.
在Python 2中做爲兩種字符串類型, str與unicode之間就須要各類轉換的方式. 首先是一種顯式轉換的方式, 就是encode和decode兩種方法. 在這裏這兩貨的意思很容易被搞反, 科學的調用方式是:
str --- decode方法 ---> unicode
unicode --- encode方法 ---> str
好比:
>>> type('x') <type 'str'> >>> type('x'.decode('utf-8')) <type 'unicode'> >>> type(u'x'.encode('utf-8')) <type 'str'>
這個邏輯是這樣的, 對於unicode字符串使用utf-8編碼進行編碼, 即調用encode('utf-8')方法生成byte數組類型的結果. 相反對於byte數組進行解碼, 生成unicode字符串. 這個新手錶示理解不能, 不過熟悉了就見怪不怪了.
另外是隱式的轉換, 和C語言中的int轉double相似, 當一個unicode字符串和一個str字符串進行鏈接的時候會默認自動將str字符串轉換成unicode類型而後再鏈接. 而這個時候使用的編碼方式則是系統所默認的編碼方式. 使用:
import sys
print sys.getdefaultencoding()
能夠獲得當前默認的編碼方式, 是否是'ascii'? 是的話就恭喜你中彩了~!! 在這個時候若是有如下一行代碼就保證會出錯:
>>> x = u'喵' >>> x u'\u55b5' >>> y = x.encode('utf-8') >>> x + y Traceback (most recent call last): File "", line 1, in UnicodeDecodeError: 'ascii' codec can't decode byte 0xe5 in position 0: ordinal not in range(128)
x是unicode類型的變量, y是x通過encode後的結果是str類型的變量. x + y的時候, 首先要將y轉換成unicode字符串, 那麼使用什麼編碼格式轉換呢, 用utf-8仍是gb2312或者仍是utf-16? 這個時候就要根據sys.getdefaultencoding()來肯定, 而sys.getdefaultencoding()是'ascii'編碼, 在ascii字符表中不存在0xe5這種大於128的字符存在, 因此固然報錯啦! 經過加入
#coding:utf-8 import sys reload(sys) sys.setdefaultencoding("utf-8")
則能夠將默認的編碼轉換格式變成utf-8, 且大多數狀況下, 程序中的字符串是經過utf-8來編碼的, 因此只要加上以上三行就能夠了.
可是有沒有以爲, 加上這些會使得代碼有些dirty? 咳, 至少對於我來講確實很dirty. 因此我以爲平時寫程序的過程當中要養成儘可能使用顯示的轉換的習慣, 而且要明確某個函數返回的究竟是str仍是unicode, 凡是str的主動decode成unicode, 不要將二者混淆掉, 這樣寫出來的代碼才比較乾淨. 此外還能夠在代碼最上方加入'from future import unicode_literals'能夠默認將用戶自定義字符串變成unicode類型.
最後我想大吼一聲Python 2.x中str不是字符串, 而是B♂Y♂T♂E♂數♂組~!!
若是一個project必須在兩個平臺上開發,程序應該使用一樣的encoding,好比要求全部的文件都使用UTF-8,若是實在不能統一(通常是爲了知足許多所謂專家學者莫名其妙的要求),能夠退而求其次,用當前系統編碼決定文件內的編碼:
import locale
import string
import re
#根據當前系統的encoding構造須要的編碼取值
lang = string.upper(locale.setlocale(locale.LC_ALL, ""))
textencoding = None 3
#檢查編碼的值是否是知足咱們須要的狀況
if re.match("UTF-8", lang) != None:
# UTF-8編碼
textencoding = "utf-8"
elif re.match(r"CHINESE|CP936", lang):
# Windows下的GB編碼
textencoding = "gb18030"
elif re.match(r"GB2312|GBK|GB18030", lang):
# Linux下的GB編碼
textencoding = "gb18030"
else:
# 其餘狀況,拋個錯誤吧
raise UnicodeError
fd = file(filename, "r")
fulltextlist = fd.readlines()
# 把每一行轉換成unicode
for each in len(fulltextlist):
fulltextlist[i] = unicode(each, textencoding)
fd.close()
# 若是要打印的話,能夠用text.encode(encoding)來恢復成多字節編碼
編碼encoding發生在Unicode字符串轉換爲字節序列時,而解碼decoding發生在字節序列轉換爲Unicode字符串時(encoding always takes a Unicode string and returns a bytes sequence, and decoding always takes a bytes sequence and returns a Unicode string)。
UnicodeDecodeError
UnicodeDncodeError一般發生在將str字符串解碼爲特定Unicode字符串時。因爲不一樣的編碼只能映射部分str字符串到對應的Unicode字符,因此遇到一些字符時解碼會失敗。
UnicodeEncodeError
UnicodeEncodeError一般發生在將Unicode字符串編碼爲特定字節序列時。因爲不一樣的編碼只能映射部分Unicode字符到對應的str字符串,因此遇到一些字符時編碼會失敗。
處理python編碼轉換時的UnicodeDecodeError異常
python提供的unicode轉換不像iconv或是mbstowcs之類的方便。若是轉換一段unicode("1234中文",'ascii') 到utf8,會直接出現UnicodeDecodeError的錯誤。若是在你能預知字串符的編碼的時候,好比你用unicode('1234中文', 'gbk') 就不會出現錯誤;不過不少時候,會出現CJK混合的狀況,若是要作到將一段CJK文件轉換成unicode可能就行不通了。好在python的codecs提供了register_error這個功能:
register_error(name, error_handler)
原理很簡單,不過要先看unicode是如何處理異常的。unicode這個函數是將一段string按輸入的編碼轉換成目標的編碼,若是出現了不與輸入編碼相符的,會出現一個UnicodeDecodeError的異常,一般有三種處理方法:strict、replace、ignore;默認是 strict,就是直接raise UnicodeDecodeError。經過register_error,咱們也能夠有本身的處理方法,若是遇到與輸入的編碼不符的時候,咱們就本身識別,好比GBK、BIG五、JP的字符。
def cjk_replace(exc):
if not isinstance(exc, UnicodeDecodeError):
raise TypeError("don't know how to handle %r" % exc)
if exc.end + 1 > len(exc.object):
raise TypeError('unknown codec ,the object too short!')
ch1 = ord(exc.object[exc.start:exc.end])
newpos = exc.end + 1
ch2 = ord(exc.object[exc.start + 1:newpos])
sk = exc.object[exc.start:newpos]
if 0x81<=ch1<=0xFE and (0x40<=ch2<=0x7E or 0x7E<=ch2<=0xFE): # GBK
return (unicode(sk,'cp936'), newpos)
if 0x81<=ch1<=0xFE and (0x40<=ch2<=0x7E or 0xA1<=ch2<=0xFE): # BIG5
return (unicode(sk,'big5'), newpos)
raise TypeError('unknown codec !')
codecs.register_error("cjk_replace", cjk_replace)
咱們的cjk_replace如今只能處理GBK與BIG5的,由於我對編碼也不是特別瞭解,只是大概知道GBK與BIG5的,不太瞭解JP的。在 cjk_replace這個函數裏,咱們對不認識的文字進行手工識別,若是認識的編碼,就用正確的方法,並返回編碼後的內容與新的pos,好比「1234中文」,在pos爲4的時候,會調用咱們的cjk_replace,咱們會返回一個從gbk轉換成utf8的「中」字,並返回下個正確的位置「文」的起始位置。固然了,處理「文」的時候,還會再調用一次。下面看看是如何使用的:
filedata = open('test.txt','r).read() #gbk and big5 file
data = unicode(filedata,'ascii','cjk_replace').encode('utf8')
小結
一個比較通常的Python中文處理的流程:
* 將欲處理的字符串用unicode函數以正確的編碼轉換爲Unicode
* 在程序中統一用Unicode字符串進行操做
* 輸出時,使用encode方法,將Unicode再轉換爲所需的編碼
有幾點要說明一下:
* 所謂「正確的」編碼,指得是指定編碼和字符串自己的編碼必須一致。這個其實並不那麼容易判斷,通常來講,咱們直接輸入的簡體中文字符,有兩種可能的編碼:GB2312(GBK、GB18030)、以及UTF-8
* encode成本地編碼的時候,必需要保證目標編碼中存在欲轉換字符的內碼。encode這種操做通常是經過一個本地編碼對應Unicode的編碼轉換表來進行的,事實上每一個本地編碼只能映射到Unicode的一部分。可是映射的區域是不一樣的,好比Big-5對應的Unicode的編碼範圍和 GBK對應的就不同(實際上這兩個編碼有部分範圍是重疊的)。因此,Unicode的一些字符(好比自己就是從GB2312轉換來的那些),能夠映射到 GBK,但未必能夠映射到Big-5,若是你想轉換到Big-5,頗有可能就會出現編碼找不到的異常。但UTF-8的碼錶範圍實際上和Unicode是同樣的(只是編碼形式不一樣而已),因此,理論上來講,任何本地編碼的字符,均可以被轉換到UTF-8
* GB23十二、GBK、GB18030本質上是同一種編碼標準。只是在前者的基礎上擴充了字符數量
* UTF-8和GB編碼不兼容
* 出現編解碼異常時可能須要本身編寫編解碼解析函數,這須要瞭解一些字符編碼的知識
一、寫的代碼模塊須要指定編碼
若是代碼沒有指定coding,python就默認全部的字符爲ASCII碼,
ASCII碼只支持256個字符,ASCII碼不支持中文,因此就報錯。
因此要在代碼前寫上#coding:utf-8或#coding:gbk
建議你的代碼統一寫成這樣:
#coding:utf-8 import sys reload(sys) sys.setdefaultencoding('utf-8')
二、python2內部全部編碼統一爲unicode
unicode能夠處理世界上全部語言的字符。
utf-8爲unicode的一種實現形式,因此須要在代碼前寫上#coding:utf-8
三、編碼轉換
牢記python2內部編碼爲unicode.
其它的編碼decode()爲unicode,再編碼encode()爲你指定的編碼,就不會出現亂碼。
四、網頁採集時
代碼指定#coding:utf-8
若是網頁的編碼爲gbk
須要這樣處理:
html = html.decode('gbk').encode('utf-8')
五、代碼前也能夠寫#coding:gbk,但也要保證你的代碼文件的保存格式爲gbk.這個在windos下會出現這樣的問題。
六、字典等key或值的漢字問題
#coding:utf-8
dict1 ={1:'python週末培訓班',2:'諮詢010-68165761 QQ:1465376564'}
print dict1
# 這樣輸出的沒有顯示漢字,是顯示漢字的其它編碼
dict2 ={1:'python視頻培訓班',2:'諮詢010-68165761 QQ:1465376564'}
for key in dict2:
print dict2[key]
七、unicode的漢字編碼寫到文本文件中
須要根據文本文件的編碼進行轉換
能夠encode('utf-8')或encode('gbk')
總結:凡是報錯信息中出現的錯誤包含「ASCII」,就是沒有指定漢字編碼的問題。
一、 http://bbs3.chinaunix.net/thread-1389703-1-2.html
二、 Python的中文處理及其它
http://www.go4pro.org/?p=38
三、 Python處理中文的時候的一些小技巧
http://cocre.com/?p=461
四、 Unicode In Python, Completely Demystified. Kumar McMillan
http://farmdev.com/talks/unicode
五、 python中文處理好方法
http://www.okpython.com/bbs/viewthread.php?tid=311
六、 Python的中文處理
http://hi.baidu.com/mrsz/blog/item/7812a5018c2cf2031d9583d2.html
七、 UnicodeDecodeError
http://wiki.python.org/moin/UnicodeDecodeError
八、 UnicodeEncodeError
http://wiki.python.org/moin/UnicodeEncodeError
九、 如何處理python編碼轉換時的UnicodeDecodeError異常
http://blog.chinaunix.net/u/8873/showart_1009737.html
十、codecs — Codec registry and base classes
http://docs.python.org/library/codecs.html
十一、python 中文亂碼 問題深刻分析
http://blog.csdn.net/kiki113/article/details/4062063
十二、python新手必碰到的問題---encode與decode,中文亂碼[轉]
http://www.51testing.com/?uid-524463-action-viewspace-itemid-817888
1三、Python 編碼和 Unicode(深刻理解 python 編碼的文章,推薦閱讀)
http://blog.jobbole.com/50345/
1四、關於python2中的一個編碼問題(u'\xe5\xb0\x8f\xe4\xb8\x89' 與 '\xe5\xb0\x8f\xe4\xb8\x89' 的區別)
http://segmentfault.com/q/1010000000370030
1五、Python的中文編碼問題
http://python.jobbole.com/80831/
1六、在Python中正確使用Unicode
http://python.jobbole.com/80939/
1七、Python字符編碼詳解
http://python.jobbole.com/82107/
1八、一個例子搞懂編碼問題(深刻淺出 python 編碼的文章,推薦閱讀)
http://bindog.github.io/blog/2014/12/16/python-coding/
1九、Python 編碼的前世此生