在编写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()
    user@host:~$ 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()
    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.斯特杜特.缓冲器为此而写;如果你试图将字节写入sys.stdout使用sys.斯特杜特.write()则会返回一个错误,如果您try 使用print()则print()只需将bytes对象转换为string对象,像\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                                                |.|
    
  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相关问答推荐

pandas查找另一列中是否存在ID

Python将类实例变量转换为嵌套 struct

按小时和日期对Pandas 数据帧进行分组

我用Kivy创建的应用程序在安卓系统上运行时出错.(attributeerror:';class';对象没有属性';_javaclass__cls_storage';)

在 Python 中比较和排序列之间的值(带有不匹配列)

基于其他列的条件向Panda数据框中添加值到新列

如何根据索引子列表对元素列表进行分组或批处理?

拆分列表的元素并将拆分后的元素包含到列表中

导入在不同目录中定义的函数

使用 python-binance 时,heroku [regex._regex_core.error: bad escape \d at position 7] 出错

请求:RecursionError:超出最大递归深度

无法在 Windows Python 3.5 上安装 Levenshtein 距离包

是否有与 Laravel 4 等效的 python?

使用 pytest.fixture 返回模拟对象的正确方法

用于 Django 应用程序的 Cython:它会工作吗?

从 csv 中删除单行而不复制文件

Selenium Python - 处理没有这样的元素异常

在 Keras 中训练神经网络的零精度

Python asyncio:处理gather()中的异常-文档不清楚?

操作列时如何用Pandas 数据框处理除以零?