轉自:http://blog.csdn.net/trochiluses/article/details/16825269
摘要:在進行python腳本的編寫時,如果我們用python來處理網頁數據或者進行與中文字符有關的處理工作,經常出現這樣的出錯信息:SyntaxError: Non-ASCII character '\xe6' in file ./filename.py on line 3, but no encoding declared。本文主要講解python中與unicode和中文、特殊字符編碼有關的問題。字符編碼和解碼需要遵循什麼規律?
前言:
如果密碼領域一樣,從明文到密碼是加密,從密碼到明文是解密。在python中,編碼:unicode-->str;解碼str-->unicode.既然是編碼,那麼就和密碼領域一樣,編碼和解碼自然涉及到編碼/解碼方案(對應加密或者解密算法),unicode相當於明文。在python中,編碼函數是encode(),解碼函數是decode()。需要注意的一點是,如果我們調用str.encode(),這裏涉及到一個隱士的類型轉化,會現將str轉化成unicode,才能進行編碼,這也是不太容易理解的地方。所以,str.encode()實際上就等價於str.decode(sys.defaultencoding).encode().而sys.defaultencoding一般是ascii,它是不能用來編碼中文字符的。
在閱讀本文之間,如果你對字符編碼不是很熟悉,有必要先了解以下字符編碼。可以參考:字符編碼簡介。
1.一箇中文字符編碼問題
一個python腳本如下:
- <span style="font-size:14px;"> #!/usr/bin/python
- string='我的'
- print string</span>
SyntaxError: Non-ASCII character '\xe6' in file ./filename.py on line 3, but no encoding declared
出錯原因:python默認採用ascii編碼,而中文編碼不再ascii編碼能夠表示的範圍之內,所以string無法將“我的”作爲ascii編碼保存爲str類型。
解決辦法:採用中文字符編碼,在腳本第二行加入編碼類型,如下:
- <span style="font-size:14px;">#!/usr/bin/python
- #coding=gbk
- string='我的'
- print string
- </span>
這裏,coding同樣可以採用utf-8等能夠編碼中文字符的模式。
2.python的對字符的編解碼
字符編碼/解碼函數:
1)unicode:這個是python的內建函數,位於unicode類。
unicode(string [, encoding[, errors]]) -> object
這個函數的作用是將string按照encoding的格式編碼成爲unicode對象。
省略參數將用python默認的ASCII來解碼
2)decode:位於unicode類中。
decode(...)
| S.decode([encoding[,errors]]) -> string or unicode
|
| Decodes S using the codec registered for encoding.
- <span style="font-size:14px;">#!/usr/bin/python
- #coding=gbk
- string='我的'
- print string
- s1=unicode(string,"gbk")
- s2=string.decode("gbk")
- print s1
- print s2
- </span>
我的
鎴戠殑
鎴戠殑
顯然,輸出好像沒有達到我們的期許的結果。爲什麼s1和s2輸出的是亂碼呢?string是str,print輸出到屏幕,這個終端採用的字符編碼有關。爲什麼string是正常的,而s1和s2都是亂碼呢?我們接下來分析.
3)decode和encode都可以用於常規字符串和unicode字符串
但是:
str.decode()和unicode.encode()是直接正規的使用。
unicode.decode()會先將unicode轉化成str,然後再執行decode()。
3.codec是什麼
Codec是把Coder/DECoder得首字母組合,它定義了文本跟二進制的轉換方式,跟ASCII那種用一個字節把字符轉換成數字的方式不同,Unicode用的是多字節,這導致了Unicode支持多種不同的編碼方式,比如說codec支持的四種耳熟能詳的編碼方式是:ASCII,ISO8859—1/Latin-1,UTF-8,和UTF-16
最著名的是UTF-8編碼,它也用一個字節來編碼ASCII字符,這讓那些必須同時處理ASCII碼和Unicode碼文本的程序員的工作變得非常輕鬆,因爲ASCII字符的UTF-8編碼和ASCII編碼完全相同。
UTF-8編碼可以用1到4個字節來表示其他語言的字符,這給那些需要直接處理Unicode數據的程序員帶來了麻煩,因爲他們沒有辦法按照固定長度逐一讀出各個字符,幸運的是我們不需要掌握直接讀取Unicode數據的方法,Python已經替我們完成了相關細節,我們無需爲處理多字節字符的複雜問題而擔心。
UTF-16也是一種變長編碼,但是它不常用。
4.編碼與解碼
Unicode支持多種編碼格式,這爲程序員帶來了額外的負擔,每當你向一個文件寫入字符串的時候,你必須定義一個編碼用於把對應的Unicode內容轉換成你定義的格式,Python通過Unicode字符串的encode()函數解決了這個問題,該函數接受字符串中的字符爲參數,輸出你指定的編碼格式的內容。
所以,每次我們寫一個Unicode字符串到磁盤上我們都要用指定的編碼器給他“編碼“一下,相應地,當我們從這個文件讀取數據時,我們必須”解碼”該文件,使之成爲Unicode字符串對象。
5.python對unicode的支持
內建的unicode()函數:將一個string類型的字符串轉變成一個unicode對象
decode/encode方法:用於將str對象轉化成unicode對象,或者相反。
來看下面這一行例子:
- <span style="font-size:14px;">#!/usr/bin/python
- #coding=gbk
- string='我的'
- print "string is:",type(string)
- print string
- ustring=u"我的"
- print "ustring is:",type(ustring)
- print ustring
- gbkstring=ustring.encode("gbk")
- print "gbkstring is:",type(gbkstring)
- print gbkstring
- anotherstring=gbkstring.decode("gbk")
- print "anotherstring is:",type(anotherstring)
- print anotherstring
- </span>
輸出結果如下:
string is: <type 'str'>
我的
ustring is: <type 'unicode'>
鎴戠殑
gbkstring is: <type 'str'>
我的
anotherstring is: <type 'unicode'>
鎴戠殑
任何兩種字符編碼之間如果想完成轉化,必須要通過unicode這個橋樑,先把它抓化成unicode對象;unicode對象直接進行輸出,往往會出現亂碼,需要解碼成str對象。另外需要注意:unicode對象,gbk編碼,ascii編碼,str對象這四個不同的概念。注意區分什麼是字符串類型,什麼是編碼類型。
6.注意事項
關於字符編碼的原理,可以參考這裏:
在python中需要使用unicode需要注意:
1 程序中出現字符串時一定要加一個前綴u
2 不要用str()函數,用Unicode()代替
3 不要用過時的string模塊。如果傳給它非ASCII碼,它會把一切搞砸。
4 不到必須時不要在你的程序裏編解碼Unicode字符,只在你要寫入文件或者數據庫或者網絡時,才調用encode()函數和decode()函數。
5.使用什麼字符編碼,就要採用對應的字符集進行解碼
內建的str()函數和chr()函數不能處理Unicode,它們只能處理常規ASCII編碼的字符串,如果一個Unicode字符串作爲參數傳給了str()函數,它會首先被轉換成ASCII碼字符串然後交給str()函數。
7.關於linux終端的字符編碼
終端等默認語言設置在/etc/environment之中,在linux下,如果terminal採用的是utf-8編碼,那麼如果我們的中文采用gbk編碼,很有可能在輸出到屏幕的時候產生亂碼。
使用locale命令,可以查看與語言有關的環境變量:
- hyk@hyk-linux:~/program/python/chapter6
- $ locale
- LANG=zh_CN.UTF-8
- LANGUAGE=zh_CN:en_US:en
- LC_CTYPE="zh_CN.UTF-8"
- LC_NUMERIC=zh_CN.UTF-8
- LC_TIME=zh_CN.UTF-8
- LC_COLLATE="zh_CN.UTF-8"
- LC_MONETARY=zh_CN.UTF-8
- LC_MESSAGES="zh_CN.UTF-8"
- LC_PAPER=zh_CN.UTF-8
- LC_NAME=zh_CN.UTF-8
- LC_ADDRESS=zh_CN.UTF-8
- LC_TELEPHONE=zh_CN.UTF-8
- LC_MEASUREMENT=zh_CN.UTF-8
- LC_IDENTIFICATION=zh_CN.UTF-8
- LC_ALL=
python的print方法,會自動將相關的字符編碼,轉化成該環境變量對應的字符編碼,所以使用print 可能會出現亂碼和報錯;但是使用write卻不會。
8.python中關於字符處理的出錯與解決辦法
問題一:
- 13 strencode=string.encode("utf-8")
- 14 print "strencode is : ",type(strencode)
- 15 print strencode
UnicodeDecodeError: 'ascii' codec can't decode
byte 0xe5 in position 4: ordinal not in range
解釋:str本身是不能encode的,如果想要encode,先要轉化成unicode,此時採用默認的ascii進行轉化,所以就出錯了。
解決辦法:
1)指明str轉化成unicode的編碼方式:
- #! /usr/bin/env python
- # -*- coding: utf-8 -*-
- s = '中文'
- s.decode('utf-8').encode('gb18030')
2)重置變量 sys.defaultencoding
- import sys
- reload(sys) # Python2.5 初始化後會刪除 sys.setdefaultencoding 這個方法,我們需要重新載入
- sys.setdefaultencoding('utf-8')
- str = '中文'
- str.encode('gb18030')
參考文獻:
【1】字符編碼簡介:http://blog.csdn.net/trochiluses/article/details/8782019