在编写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相关问答推荐

Paramiko SFTPClient get()和put()函数的通过/失败结果?

数组列的极点成对求和

如何在M x N数组的行中找到所有值的组合

如何使用 Selenium Python 连续单击一个按钮直到另一个元素出现?

替换 .txt 文件中的项目列表

两个 y 轴在零处对齐的 plotly barplot

无法提出给定 for 循环的原因 (Python 3.11)

Pandas 窗口聚合两个排序表

如何对具有多个列值的 pandas 数据框进行数据透视/数据透视表

为什么 Sympy 不能解决我的非线性系统? Python 解释器一直在执行,直到我终止进程

如果网站加载时间过长,如何强制 Selenium 刷新

使用正确的数据类型时,使用 Cerberus 验证 JSON 架构会引发错误

spinbutton调整up/down箭头

Python过滤器函数 - 单个结果

全局捕获快速 api 中的异常

如何通过命令行将数组传递给python

Python:在 map 对象上调用列表两次

如何将二进制(字符串)转换为浮点值?

为什么 TensorFlow 的 `tf.data` 包会减慢我的代码速度?

TypeError: write() 参数必须是 str,而不是字节(Python 3 vs Python 2)