在编写Python 3.1 CGI脚本时,我遇到了可怕的UnicodeDecodeErrors.但是,在命令行上运行脚本时,一切都正常.

似乎open()print()使用返回值locale.getpreferredencoding()来知道默认情况下使用什么编码.在命令行上运行时,该值应为"UTF-8".但当通过浏览器运行脚本时,编码会神秘地被重新定义为"ANSI_X3".4-1968",这似乎只是普通ASCII的一个花哨名称.

我现在需要知道如何让cgi脚本在所有情况下都以"utf-8"作为默认编码运行.我的设置是在Debian Linux上的Python 3.1.3和Apache2.系统范围的语言环境是en_GB.utf-8.

推荐答案

为后来者回答这个问题,因为我不认为发布的答案能够找到问题的根源,即CGI上下文中缺少区域设置环境变量.我使用的是Python 3.2.

  1. open()以文本(字符串)或二进制(字节)模式打开文件对象进行读取和/或写入;在文本模式下,可在调用中指定用于编码写入文件的字符串和解码从文件读取的字节的编码;如果不是,则由语言环境决定.getpreferredencoding(),它在linux上使用本地环境设置中的编码,通常是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()
    [email protected]:~$ hd foo
    00000000  e2 82 ac      |...|    # data is UTF-8 encoded
    
  2. 系统.stdout实际上是一个以文本模式打开的文件,其编码基于区域设置.getpreferredencoding();你可以很好地向它写入字符串,它们将根据sys编码为字节.stdout编码;默认情况下,print()会写入sys.stdout-print()本身没有编码,而是它写入的文件有编码;

    >>> sys.stdout.encoding
    'UTF-8'                          # encoding is from the environment
    >>> exit()
    [email protected]:~$ python3 -c 'print("€")' > foo
    [email protected]:~$ hd foo
    00000000  e2 82 ac 0a   |....|   # data is UTF-8 encoded; \n is from print()
    

    ; 不能向sys写入字节.stdout-使用sys.斯特杜特.缓冲器为此而写;如果你试图将字节写入sys.stdout使用sys.斯特杜特.write()则会返回一个错误,如果您try 使用print()则print()只需将bytes对象转换为string对象,像\xff这样的转义序列将被视为四个字符\,x,f,f

    [email protected]:~$ python3 -c 'print(b"\xe2\xf82\xac")' > foo
    [email protected]:~$ 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                                                |.|
    
  3. 在CGI脚本中,您需要写入sys.stdout,您可以使用print()来实现;但是Apache中的CGI脚本进程没有语言环境设置——它们不是CGI规范的一部分;因此,系统.标准输出编码默认为ANSI_X3.4-1968年——换句话说,ASCII;如果试图将包含非ASCII字符的字符串打印到sys.stdout您将得到"UnicodeEncodeError:'ascii'编解码器无法对字符进行编码…":序号不在范围内(128)

  4. 一个简单的解决方案是在服务器或虚拟主机配置中使用Apache的mod_env PassEnv命令将Apache进程的LANG环境变量传递给CGI脚本:PassEnv LANG;在Debian/Ubuntu上,确保在/etc/apache2/envvars中取消了"/etc/default/locale"行的注释,以便Apache使用系统默认语言环境运行,而不是C(Posix)语言环境(这也是ASCII编码);在Python 3.2中运行以下CGI脚本时应无错误:

    #!/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>')
    

          

Python-3.x相关问答推荐

为什么最简单的流光示例会出错?

为什么我不能通过索引获取字典键?

如何查找 tensorflow.python.data.ops.dataset_ops.MapDataset 对象的大小或形状,make_csv_dataset 的输出

“警告:scheme.data 的值不匹配”当我try 更新 pip 或安装软件包时

在带有 M1 芯片(基于 ARM 的 Apple Silicon)的 Mac 上安装较早版本的 Python(3.8 之前)失败

Python 3.x 中的 NoneType 在哪里?

Python 无法安装包

如何在python3中导入cv2?

Python 请求很慢并且需要很长时间才能完成 HTTP 或 HTTPS 请求

判断 dict.items() 中的成员资格的时间复杂度是多少?

如何在继承的数据类中创建可选字段?

如何从另一个目录导入 python 包?

压缩 Python 生成器,第二个更短:如何检索被静默消耗的元素

如何在导入 win32api 时修复“ImportError: DLL load failed”

在 Docker 中使用 PYTHONDONTWRITEBYTECODE 有什么缺点吗?

“with”语句是否支持类型提示?

无法在仅 tensorflow CPU 安装上加载动态库“cudart64_101.dll”

*(单星)和/(斜线)作为独立参数有什么作用?

= (equal) 在表达式大括号内的 f 字符串中做什么?

替换 tensorflow v2 的占位符