2012-08-01 163 views
10

如果在控制檯中運行此代碼 - 它運行良好(它是俄語),但是如果在Apache2服務器上像cgi那樣運行它 - 它會失敗:<type 'exceptions.UnicodeEncodeError'>: 'ascii' codec can't encode characters in position 8-9: ordinal not in range(128)。該代碼是:爲什麼python-cgi在unicode上失敗?

#!/usr/bin/env python 
# -*- coding: UTF-8 -*- 

import cgitb 
cgitb.enable() 

print "Content-Type: text/html;charset=utf-8" 
print 
s=u'Nikolja \u043d\u0435 \u0421\u0430\u0440\u043a\u043e\u0437\u0438!' 
print s#.encode('utf-8') 

是,解決方案是取消註釋.encode('utf-8'),但我花更多的時間去了解爲什麼會發生比,我不能看到答案。

回答

10

從控制檯的Python運行可以檢測控制檯的編碼何時以及隱式轉換的Unicode打印到控制檯到該編碼。如果該編碼不支持您嘗試打印的字符,它仍可能失敗。 UTF-8可以支持所有Unicode字符,但美國Windows上的其他常用控制檯編碼(例如cp437)則不支持。

當stdout不是控制檯時,當Python 2.X無法確定控制檯編碼時默認爲ASCII。這就是爲什麼在網絡服務器中,你必須明確並自己編碼輸出。

舉個例子,從一個控制檯,並從網絡服務器試試下面的腳本:

import sys 
print sys.stdout.encoding 

從你應該得到一些編碼控制檯,但是從網絡服務器,你應該得到None。請注意,Python 2.X使用ascii,但Python 3.X在編碼無法確定時使用utf-8

重定向輸出時,控制檯也可能出現此問題。這個腳本:

import sys 
print >>sys.stderr,sys.stdout.encoding 
print >>sys.stderr,sys.stderr.encoding 

返回時對重定向stdout直接運行以下命令:

C:\>test 
cp437 
cp437 

C:\>test >out.txt 
None 
cp437 

注意stderr並沒有受到影響,因爲它是不重定向。

環境變量PYTHONIOENCODING也可用於覆蓋默認的stdout/stdin編碼。

5

嘗試在stdin和stdout施加UTF-8編碼解碼器...

#!/usr/bin/env python 
# -*- coding: UTF-8 -*- 

import cgitb 
import sys 
import codecs 

reload(sys) 
sys.setdefaultencoding('utf-8') 
sys.stdout = codecs.getwriter('utf-8')(sys.stdout) 
# If you need input too, read from char_stream as you would sys.stdin 
char_stream = codecs.getreader('utf-8')(sys.stdin) 

cgitb.enable() 

print "Content-Type: text/html;charset=utf-8" 
print 
s=u'Nikolja \u043d\u0435 \u0421\u0430\u0440\u043a\u043e\u0437\u0438!' 
print s.encode('utf-8') 
+0

你沒有解釋爲什麼會發生這種情況...... – scythargon 2012-08-01 20:33:36

+2

不建議更改默認編碼。它打破了依靠默認編碼的庫。如果你使用'codecs.getwriter'重新映射'stdout',那麼在任何情況下都不需要。 – 2012-08-01 21:19:41

+0

我同意馬克,我不需要也不想要行sys.setdefaultencoding('utf-8')。 – DrSkippy 2012-08-15 18:06:20