在Python 3 CGI脚本中设置编码
在编写Python 3.1 CGI脚本时,我遇到了可怕的UnicodeDecodeErrors。但是,在命令行上运行脚本时,一切正常 似乎在Python 3 CGI脚本中设置编码,python,unicode,python-3.x,cgi,Python,Unicode,Python 3.x,Cgi,在编写Python 3.1 CGI脚本时,我遇到了可怕的UnicodeDecodeErrors。但是,在命令行上运行脚本时,一切正常 似乎open()和print()使用了locale.getpreferredencoding()的返回值来了解默认情况下要使用的编码。在命令行上运行时,该值应为“UTF-8”。但当通过浏览器运行脚本时,编码会神秘地被重新定义为“ANSI_X3.4-1968”,这似乎只是普通ASCII的一个花哨名称 我现在需要知道如何使cgi脚本在所有情况下都以“utf-8”作为默
open()
和print()
使用了locale.getpreferredencoding()
的返回值来了解默认情况下要使用的编码。在命令行上运行时,该值应为“UTF-8”。但当通过浏览器运行脚本时,编码会神秘地被重新定义为“ANSI_X3.4-1968”,这似乎只是普通ASCII的一个花哨名称
我现在需要知道如何使cgi脚本在所有情况下都以“utf-8”作为默认编码运行。我的设置是在DebianLinux上的Python 3.1.3和Apache2。系统范围的语言环境是en_GB.utf-8。最好的办法是使用您想要使用的编码将Unicode字符串显式编码为字节。依赖隐式转换将导致类似这样的问题
顺便说一句:如果错误真的是Unicode DecodeError,那么它不会发生在输出上,而是试图将字节流解码为Unicode,这可能发生在其他地方。我用以下代码解决了我的问题:
import locale # Ensures that subsequent open()s
locale.getpreferredencoding = lambda: 'UTF-8' # are UTF-8 encoded.
import sys
sys.stdin = open('/dev/stdin', 'r') # Re-open standard files in UTF-8
sys.stdout = open('/dev/stdout', 'w') # mode.
sys.stderr = open('/dev/stderr', 'w')
这个解决方案并不漂亮,但似乎暂时可行。实际上,我选择了Python 3而不是更普通的v。2.6作为我的开发平台,由于宣传良好的Unicode处理,但是cgi
包似乎破坏了一些简单性
我相信
/dev/std*
文件可能不存在于没有procfs的旧系统上。但是,它们在最近的Linux上是受支持的。您不应该将IO流作为CGI/WSGI的字符串来读取;它们不是Unicode字符串,而是显式的字节序列
(假设内容长度
是以字节而非字符为单位测量的;想象一下,尝试读取多部分/表单数据
二进制文件上传提交压缩成UTF-8解码字符串,或返回二进制文件下载…)
因此,使用sys.stdin.buffer
和sys.stdout.buffer
获取stdio的原始字节流,并用它们读/写二进制文件。在适当的情况下,由表单读取层使用网页上的编码将这些字节转换为Unicode字符串参数
不幸的是,标准库CGI和WSGI接口在Python3.1中没有做到这一点:相关模块是使用2to3
从Python2原始版本粗略转换而来的,因此有许多bug最终会出现在UnicodeError中
可用于web应用程序的Python 3的第一个版本是3.2。使用3.0/3.1简直是浪费时间。令人遗憾的是,花了很长时间才解决了这个问题,并通过了PEP33333。为后来者回答这个问题,因为我不认为发布的答案能够找到问题的根源,即CGI上下文中缺少区域设置环境变量。我正在使用Python 3.2
>>> 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 |.|
#!/usr/bin/env python3
import sys
print('Content-Type: text/html; charset=utf-8')
print()
print('<html><body><pre>' + sys.stdout.encoding + '</pre>h€lló wörld<body></html>')
#/usr/bin/env蟒蛇3
导入系统
打印('Content-Type:text/html;charset=utf-8')
打印()
打印(''+sys.stdout.encoding+'h€llówörld')
总结@cercatrova的答案:
- 将
行添加到PassEnv LANG
或/etc/apache2/apache2.conf
的末尾.htaccess
- 取消注释
中的行/etc/default/locale
/etc/apache2/envvars
- 确保类似于
的行出现在LANG=“en_US.UTF-8”
中/etc/default/locale
sudo服务apache2重启
- 我遇到了同样的问题。我的环境是Windows10+Apache 2.4+Python 3.8。
我正在为Google Earth Pro开发一个覆盖图,它只访问
#!/usr/bin/env python3
import sys
print('Content-Type: text/html; charset=utf-8')
print()
print('<html><body><pre>' + sys.stdout.encoding + '</pre>h€lló wörld<body></html>')
sys.stdout = codecs.getwriter('utf8')(sys.stdout.buffer)
SetEnv PYTHONIOENCODING utf8
Options +ExecCGI
AddHandler cgi-script .py