重现步骤:
- 创建包含内容
This is 中文
的文件test.txt
(即 UTF-8 编码的非 ASCII 文本)。
在 - 英特尔爱迪生上自定义编译 python 3.5.2。
启动自定义编译的 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)
在"常规"Linux系统上,通过设置适当的语言环境可以轻松解决此问题,例如请参阅此帖子或该帖子。但是,在英特尔爱迪生上,我无法设置LC_CTYPE
,因为默认的 Yocto Linux 发行版缺少语言环境(例如,请参阅此页面)。
我还尝试使用其他一些技巧,例如
import sys; sys.getfilesystemencoding = lambda: 'UTF-8'
import locale; locale.getpreferredencoding = lambda: 'utf-8'
我尝试在启动 python 解释器之前设置PYTHONIOENCODING=utf8
环境变量。
但是,这些都不起作用。唯一的解决方法是将编码显式指定为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
引导模块。更多内容见下文。PYTHONIOENCODING
仅适用于sys.stdin
、sys.stdout
和sys.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'