为什么python-cgi在unicode上失败?

scy*_*gon 10 python unicode cgi

如果在控制台中运行此代码 - 它运行良好(它是俄语),但如果在Apache2服务器上运行它像cgi - 它失败:<type 'exceptions.UnicodeEncodeError'>: 'ascii' codec can't encode characters in position 8-9: ordinal not in range(128).代码是:

#!/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')
Run Code Online (Sandbox Code Playgroud)

是的,解决方案是取消注释.encode('utf-8'),但我花了更多的时间来理解为什么不会发生,我无法看到答案.

Mar*_*nen 10

从控制台运行时,Python可以检测控制台的编码,并隐式地将打印到控制台的Unicode转换为该编码.如果该编码不支持您尝试打印的字符,它仍然可能失败.UTF-8可以支持所有Unicode字符,但是其他常见的控制台编码(如美国Windows上的cp437)则不支持.

当stdout不是控制台时,Python 2.X在无法确定控制台编码时默认为ASCII.这就是为什么在Web服务器中你必须明确并自己编码输出.

例如,从控制台和Web服务器尝试以下脚本:

import sys
print sys.stdout.encoding
Run Code Online (Sandbox Code Playgroud)

从控制台你应该得到一些编码,但你应该从Web服务器None.请注意,Python 2.X使用,ascii但Python 3.X utf-8在无法确定编码时使用.

重定向输出时,控制台上也可能出现此问题.这个脚本:

import sys
print >>sys.stderr,sys.stdout.encoding
print >>sys.stderr,sys.stderr.encoding
Run Code Online (Sandbox Code Playgroud)

直接运行与重定向时返回以下内容stdout:

C:\>test
cp437
cp437

C:\>test >out.txt
None
cp437
Run Code Online (Sandbox Code Playgroud)

注意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')
Run Code Online (Sandbox Code Playgroud)

  • 建议不要更改默认编码.它打破了依赖于默认编码的库.如果你用`codecs.getwriter`重新映射`stdout`,那么在任何情况下都不需要它. (2认同)