在 Intel Edison 上为 python 3 设置文件系统编码

Set filesystem encoding for python 3 on the Intel Edison

重现步骤:

  1. 创建一个文件 test.txt,内容为 This is 中文(即 UTF-8 编码的非 ASCII 文本)。
  2. 在 Intel Edison 上自定义编译 python 3.5.2。
  3. 启动自定义编译的 python3 解释器并发出以下代码:

    with open('test.txt', 'r') as fh:
        fh.readlines()
    

实际行为:

抛出 UnicodeDecodeError 异常。默认情况下,文件以 'ASCII' 而不是 'UTF-8' 打开:

UnicodeDecodeError: 'ascii' codec can't decode byte 0xe4 in position 8: ordinal not in range(128)

在 "regular" Linux 系统上,这个问题很容易通过设置合适的语言环境来解决,参见例如this post or that post. On the Intel Edison, however, I cannot set the LC_CTYPE since the default Yocto Linux distribution is missing locales (see e.g. this page).

我还尝试使用其他一些技巧,例如

import sys; sys.getfilesystemencoding = lambda: 'UTF-8'
import locale; locale.getpreferredencoding = lambda: 'utf-8'

我尝试在启动 python 解释器之前设置 PYTHONIOENCODING=utf8 环境变量。

但是,none 是有效的。唯一的解决方法是将编码明确指定为 open 命令的命令行参数。这适用于上面的代码片段,但它不会为我正在使用的所有包设置系统范围的默认值(这将隐式地将文件打开为 ASCII,并且可能会或可能不会为我提供覆盖该默认行为的方法)。

设置 python 解释器默认文件系统编码的正确方法是什么? (当然没有安装不需要的系统范围的语言环境。)

您可以设置 LC_ALL 环境变量来改变默认值:

$ python3 -c 'import locale; print(locale.getpreferredencoding())'
UTF-8
$ LC_ALL='.ASCII' python3 -c 'import locale; print(locale.getpreferredencoding())'
US-ASCII

我在 OS X 和 CentOS 7.

上都进行了测试

至于您的其他尝试,以下是它们不起作用的原因:

  • sys.getfilesystemencoding() 仅适用于文件名(例如 os.listdir() 和朋友)。
  • io 模块实际上并没有使用 locale.getpreferrredencoding() 函数,因此更改模块上的函数不会有任何效果。而是使用轻量级的 _bootlocale.py bootstrap module。更多内容见下文。
  • PYTHONIOENCODING 仅适用于 sys.stdinsys.stdoutsys.stdstderr

如果设置环境变量最终失败,你仍然可以修补_bootlocale模块:

import _bootlocale

old = _bootlocale.getpreferredencoding  # so you can restore
_bootlocale.getpreferredencoding = lambda *args: 'UTF-8'

这对我有用(再次在 OS X 和 CentOS 7 上,用 3.6 测试):

>>> import _bootlocale
>>> open('/tmp/test.txt', 'w').encoding  # showing pre-patch setting
'UTF-8'
>>> old = _bootlocale.getpreferredencoding
>>> _bootlocale.getpreferredencoding = lambda *a: 'ASCII'
>>> open('/tmp/test.txt', 'w').encoding  # gimped hook
'ASCII'