轉自:http://blog.csdn.net/a921800467b/article/details/8579510html
爲何會報錯「UnicodeEncodeError:'ascii' codec can't encode characters in position 0-1: ordinal notin range(128)」?本文就來研究一下這個問題。
字符串在Python內部的表示是unicode編碼,所以,在作編碼轉換時,一般須要以unicode做爲中間編碼,即先將其餘編碼的字符串解碼(decode)成unicode,再從unicode編碼(encode)成另外一種編碼。python
decode的做用是將其餘編碼的字符串轉換成unicode編碼,如str1.decode('gb2312'),表示將gb2312編碼的字符串str1轉換成unicode編碼。linux
encode的做用是將unicode編碼轉換成其餘編碼的字符串,如str2.encode('gb2312'),表示將unicode編碼的字符串str2轉換成gb2312編碼。數據庫
所以,轉碼的時候必定要先搞明白,字符串str是什麼編碼,而後decode成unicode,而後再encode成其餘編碼apache
代碼中字符串的默認編碼與代碼文件自己的編碼一致。windows
如:s='中文'瀏覽器
若是是在utf8的文件中,該字符串就是utf8編碼,若是是在gb2312的文件中,則其編碼爲gb2312。這種狀況下,要進行編碼轉換,都須要先用decode方法將其轉換成unicode編碼,再使用encode方法將其轉換成其餘編碼。一般,在沒有指定特定的編碼方式時,都是使用的系統默認編碼建立的代碼文件。tomcat
若是字符串是這樣定義:s=u'中文'網絡
則該字符串的編碼就被指定爲unicode了,即python的內部編碼,而與代碼文件自己的編碼無關。所以,對於這種狀況作編碼轉換,只須要直接使用encode方法將其轉換成指定編碼便可。socket
若是一個字符串已是unicode了,再進行解碼則將出錯,所以一般要對其編碼方式是否爲unicode進行判斷:
isinstance(s,unicode)#用來判斷是否爲unicode
用非unicode編碼形式的str來encode會報錯
如何得到系統的默認編碼?
#!/usr/bin/env python
#coding=utf-8
import sys
printsys.getdefaultencoding()
該段程序在英文WindowsXP上輸出爲:ascii
該段程序在英文Windows7上輸出爲:mbcs
在某些IDE中,字符串的輸出老是出現亂碼,甚至錯誤,實際上是因爲IDE的結果輸出控制檯自身不能顯示字符串的編碼,而不是程序自己的問題。
如在UliPad中運行以下代碼:
s=u"中文"
print s
會提示:UnicodeEncodeError:'ascii' codec can't encode characters in position 0-1: ordinal notinrange(128)。這是由於UliPad在英文WindowsXP上的控制檯信息輸出窗口是按照ascii編碼輸出的(英文系統的默認編碼是ascii),而上面代碼中的字符串是Unicode編碼的,因此輸出時產生了錯誤。
將最後一句改成:prints.encode('gb2312')
則能正確輸出「中文」兩個字。
若最後一句改成:prints.encode('utf8')
則輸出:\xe4\xb8\xad\xe6\x96\x87,這是控制檯信息輸出窗口按照ascii編碼輸出utf8編碼的字符串的結果。
unicode(str,'gb2312')與str.decode('gb2312')是同樣的,都是將gb2312編碼的str轉爲unicode編碼
使用str.__class__能夠查看str的編碼形式
>>>>>
groups.google.com/group/python-cn/browse_thread/thread/be4e4e0d4c3272dd
-----
python是個容易出現編碼問題的語言。因此,我按照個人理解寫下下面這些文字。
=首先,要了解幾個概念。=
*字節:計算機數據的表示。8位二進制。能夠表示無符號整數:0-255。下文,用「字節流」表示「字節」組成的串。
*字符:英文字符「abc」,或者中文字符「你我他」。字符自己不知道如何在計算機中保存。下文中,會避免使用「字符串」這個詞,而用「文本」來表
示「字符」組成的串。
*編碼(動詞):按照某種規則(這個規則稱爲:編碼(名詞))將「文本」轉換爲「字節流」。(在python中:unicode變成str)
*解碼(動詞):將「字節流」按照某種規則轉換成「文本」。(在python中:str變成unicode)
**實際上,任何東西在計算機中表示,都須要編碼。例如,視頻要編碼而後保存在文件中,播放的時候須要解碼才能觀看。
unicode:unicode定義了,一個「字符」和一個「數字」的對應,可是並無規定這個「數字」在計算機中怎麼保存。(就像在C中,一個整數既
能夠是int,也能夠是short。unicode沒有規定用int仍是用short來表示一個「字符」)
utf8:unicode實現。它使用unicode定義的「字符」「數字」映射,進而規定了,如何在計算機中保存這個數字。其它的utf16等都是
unicode實現。
gbk:相似utf8這樣的「編碼」。可是它沒有使用unicode定義的「字符」「數字」映射,而是使用了另外一套的映射方法。並且,它還定義瞭如何在
計算機中保存。
=python中的encode,decode方法=
首先,要知道encode是 unicode轉換成str。decode是str轉換成unicode。
下文中,u表明unicode類型的變量,s表明str類型的變量。
u.encode('...')基本上老是能成功的,只要你填寫了正確的編碼。就像任何文件均可以壓縮成zip文件。
s.decode('...')常常是會出錯的,由於str是什麼「編碼」取決於上下文,當你解碼的時候須要確保s是用什麼編碼的。就像,打開zip文
件的時候,你要確保它確實是zip文件,而不只僅是僞造了擴展名的zip文件。
u.decode(),s.encode()不建議使用,s.encode至關於s.decode().encode()首先用默認編碼(通常是
ascii)轉換成unicode在進行encode。
=關於#coding=utf8=
當你在py文件的第一行中,寫了這句話,並確實按照這個編碼保存了文本的話,那麼這句話有如下幾個功能。
1.使得詞法分析器能正常運做,對於註釋中的中文不報錯了。
2.對於u"中文"這樣literal string能知道兩個引號中的內容是utf8編碼的,而後能正確轉換成unicode
3."中文"對於這樣的literalstring你會知道,這中間的內容是utf8編碼,而後就能夠正確轉換成其它編碼或unicode了。
沒有寫完,先碼那麼多字,之後再來補充,這裏不是wiki,太麻煩了。
>>>>>
>>>>>
=Python編碼和Windows控制檯=
我發現,不少初學者出錯的地方都在print語句,這牽涉到控制檯的輸出。我不瞭解linux,因此只說控制檯的。
首先,Windows的控制檯確實是unicode(utf16_le編碼)的,或者更準確的說使用字符爲單位輸出文本的。
可是,程序的執行是能夠被重定向到文件的,而文件的單位是「字節」。
因此,對於C運行時的函數printf之類的,輸出必須有一個編碼,把文本轉換成字節。多是爲了兼容95,98,
沒有使用unicode的編碼,而是mbcs(不是gbk之類的)。
windows的mbcs,也就是ansi,它會在不一樣語言的windows中使用不一樣的編碼,在中文的windows中就是gb系列的編碼。
這形成了同一個文本,在不一樣語言的windows中是不兼容的。
如今咱們知道了,若是你要在windows的控制檯中輸出文本,它的編碼必定要是「mbcs」。
對於python的unicode變量,使用print輸出的話,會使用sys.getfilesystemencoding()返回的編碼,把它變成str。
若是是一個utf8編碼str變量,那麼就須要 prints.decode('utf8').encode('mbcs')
最後,對於str變量,file文件讀取的內容,urllib獲得的網絡上的內容,都是以「字節」形式的。
它們若是確實是一段「文本」,好比你想print出來看看。那麼你必須知道它們的編碼。而後decode成unicode。
如何知道它們的編碼:
1.事先約定。(好比這個文本文件就是你本身用utf8編碼保存的)
2.協議。(python文件第一行的#coding=utf8,html中的<meta>等)
2.猜。
>>>>>
"最後,對於str變量,file文件讀取的內容,urllib獲得的網絡上的內容,都是以「字節」形式的。"
雖然文件或者網頁是文本的,可是在保存或者傳輸時已經被編碼成bytes了,因此用"rb"打開的file和從socket讀取的流是基於字節的.
"它們若是確實是一段「文本」,好比你想print出來看看。那麼你必須知道它們的編碼。而後decode成unicode。"
這裏的加引號的"文本",其實仍是字節流(bytes),而不是真正的文本(unicode),只是說明咱們知道他是能夠解碼成文本的.
在解碼的時候,若是是基於約定的,那就能夠直接從指定地方讀取如BOM或者python文件的指定coding或者網頁的meta,就能夠正確解碼,
可是如今不少文件/網頁雖然指定了編碼,可是文件格式實際卻使用了其餘的編碼(好比py文件指定了coding=utf8,可是你仍是能夠保存成ansi--記事本的默認編碼),這種狀況下真實的編碼就須要去猜了
解碼了的文本只存在運行環境中,若是你須要打印/保存/輸出給數據庫/網絡傳遞,就又須要一次編碼過程,這個編碼與上面的編碼沒有關係,只是依賴於你的選擇,可是這個編碼也不是能夠隨便選擇的,由於編碼後的bytes若是又須要傳遞給其餘人/環境,那麼若是你的編碼也不遵循約定,又給下一我的/環境形成了困擾,因而遞歸之~~~~
>>>>>
>通常人會認爲Unicode(廣義)統一了編碼,其實否則。Unicode不是惟一的編碼,而一大堆編碼的統稱。可是Windows下Unicode
> (狹義)通常特指UCS2,也就是UTF-16/LE
unicode做爲字符集(ucs)是惟一的,編碼方案(utf)纔是有不少種
>>>>>
將字符與字節的概念區分開來是很重要的。Java一直就是這樣,Python也開始這麼作了,Ruby貌似還在混亂當中。
>>>>>
>>>>>
我也說兩句。我對編碼的研究相對比較深一些。由於工做中也常常遇到亂碼,因而在05年,對編碼專門作過研究,並在公司刊物上發過文章,最後造成了一個教材,每一年在公司給新員工都講一遍。因而項目中遇到亂碼的問題就能很快的定位並解決了。
理論上,從一個字符到具體的編碼,會通過如下幾個概念。
字符集(Abstract character repertoire)
編碼字符集(Coded character set)
字符編碼方式(Character encoding form)
字符編碼方案(Character encoding scheme )
字符集:就算一堆抽象的字符,如全部中文。字符集的定義是抽象的,與計算機無關。
編碼字符集:是一個從整數集子集到字符集抽象元素的映射。即給抽象的字符編上數字。如gb2312中的定義的字符,每一個字符都有個整數和它對應。一個整數只對應着一個字符。反過來,則不必定是。這裏所說的映射關係,是數學意義上的映射關係。編碼字符集也是與計算機無關的。unicode字符集也在這一層。
字符編碼方式:這個開始與計算機有關了。編碼字符集的編碼點在計算機裏的具體表現形式。通俗的說,意思就是怎麼樣才能將字符所對應的整數的放進計算機內存,或文件、或網絡中。因而,不一樣人有不一樣的實現方式,所謂的萬碼奔騰,就是指這個。gb2312,utf-8,utf-16,utf-32等都在這一層。
字符編碼方案:這個更加與計算機密切相關。具體是與操做系統密切相關。主要是解決大小字節序的問題。對於UTF-16和UTF-32
編碼,Unicode都支持big-endian 和 little-endian兩種編碼方案。
通常來講,咱們所說的編碼,都在第三層完成。具體到一個軟件系統中,則很複雜。
瀏覽器-apache-tomcat(包括tomcat內部的jsp編碼、編譯,文件讀取)-數據庫之間,只要存在數據交互,就有可能發生編碼不一致,若是在讀取數據時,沒有正確的decode和encode,出現亂碼就是屢見不鮮了。