为后来者回答这个问题,是因为我认为发布的答案并不能解决问题的根源,因为在CGI上下文中缺少语言环境环境变量。我正在使用Python 3.2。
open()以文本(字符串)或二进制(字节)模式打开文件对象以进行读取和/或写入;在文本模式下,可以在调用中指定用于编码写入文件的字符串以及解码从文件读取的字节的编码;如果不是,则由locale.getpreferredencoding()确定,在Linux上,locale.getpreferredencoding()使用您的语言环境设置中的编码,通常为utf-8(例如LANG = en_US.UTF-8)
>>> f = open('foo', 'w') # open file for writing in text mode
f.encoding ‘UTF-8’ # encoding is from the environment f.write(‘€’) # write a Unicode string 1 f.close() exit() user@host:~$ hd foo 00000000 e2 82 ac |…| # data is UTF-8 encoded
sys.stdout实际上是一个打开的文件,可以使用基于locale.getpreferredencoding()的编码以文本模式写入。您可以向它写字符串,然后根据sys.stdout的编码将它们编码为字节;默认情况下,print()写入sys.stdout-print()本身没有编码,而是它写入的文件具有编码;
>>> sys.stdout.encoding
‘UTF-8’ # encoding is from the environment
exit() user@host:~$ python3 -c ‘print(“€”)’ > foo user@host:~$ hd foo 00000000 e2 82 ac 0a |....| # data is UTF-8 encoded; \n is from print()
; 您不能将字节写入sys.stdout-为此使用sys.stdout.buffer.write(); 如果尝试使用sys.stdout.write()将字节写入sys.stdout,则它将返回错误,如果尝试使用print(),则print()会将字节对象简单地转换为字符串对象和转义符像这样的序列\xff
将被视为四个字符\,x,f,f
user@host:~$ python3 -c 'print(b"\xe2\xf82\xac")' > foo
user@host:~$ hd foo
00000000 62 27 5c 78 65 32 5c 78 66 38 32 5c 78 61 63 27 |b'\xe2\xf82\xac'|
00000010 0a |.|
在CGI脚本中,您需要写入sys.stdout,并且可以使用print()来完成此操作;但是Apache中的CGI脚本过程没有语言环境设置-它们不属于CGI规范;因此sys.stdout编码默认为ANSI_X3.4-1968-换句话说,是ASCII;如果您尝试将包含非ASCII字符的字符串print()传送到sys.stdout,则会收到“ UnicodeEncodeError:’ascii’编解码器无法编码字符…:序数不在范围内(128)”
一个简单的解决方案是使用服务器或虚拟主机配置中的Apache的mod_env PassEnv命令将Apache进程的LANG环境变量传递到CGI脚本。在Debian / Ubuntu上,确保在/ etc / apache2 / envvars中取消注释“。/ etc / default / locale”行,以便Apache以系统默认语言环境而不是C(Posix)语言环境(也是ASCII)运行编码);以下CGI脚本应在Python 3.2中正确运行:
#!/usr/bin/env python3
import sys print(‘Content-Type: text/html; charset=utf-8’) print() print(‘
‘ + sys.stdout.encoding + ‘