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

Python 3.x 在Intel Edison上为python 3设置文件系统编码,python-3.x,character-encoding,intel-edison,Python 3.x,Character Encoding,Intel Edison,复制步骤: 创建一个文件test.txt,内容如下中文(即UTF-8编码的非ASCII文本) 在Intel Edison上自定义编译python 3.5.2 启动自定义编译的python3解释器并发出以下代码: with open('test.txt', 'r') as fh: fh.readlines() 实际行为: 将引发UnicodeDecodeError异常。默认情况下,文件以“ASCII”而不是“UTF-8”打开: UnicodeDecodeError: 'ascii' c

复制步骤:

  • 创建一个文件
    test.txt
    ,内容如下中文(即UTF-8编码的非ASCII文本)
  • 在Intel Edison上自定义编译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系统上,通过设置适当的区域设置可以轻松解决此问题,请参阅或。但是,在Intel Edison上,我无法设置
    LC_CTYPE
    ,因为默认的Yocto Linux发行版缺少区域设置(请参见示例)

    我还尝试使用了一些其他的黑客,比如

    import sys; sys.getfilesystemencoding = lambda: 'UTF-8'
    import locale; locale.getpreferredencoding = lambda: 'utf-8'
    
    在启动python解释器之前,我尝试设置
    pythonionecoding=utf8
    环境变量

    然而,这些都不起作用。唯一的解决方法是将编码明确指定为
    open
    命令的命令行参数。这适用于上面的代码段,但它不会为我使用的所有软件包设置系统范围的默认值(这将以ASCII格式隐式打开文件,可能会也可能不会为我提供覆盖默认行为的方法)


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

    您可以设置
    LC\u ALL
    环境变量来更改默认值:

    $ python3 -c 'import locale; print(locale.getpreferredencoding())'
    UTF-8
    $ LC_ALL='.ASCII' python3 -c 'import locale; print(locale.getpreferredencoding())'
    US-ASCII
    
    我在OSX和CentOS7上都进行了测试

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


    • sys.getfilesystemencoding()
      仅适用于文件名(例如
      os.listdir()
      和好友)
    • io
      模块实际上没有使用
      locale.getpreferredencoding()
      函数,因此更改模块上的函数不会产生效果。而是使用了一个轻量级的。下面将详细介绍

    • PYTHONIOENCODING
      仅适用于
      sys.stdin
      sys.stdout
      sys.stdsterr
    如果设置环境变量最终失败,您仍然可以修补
    \u bootlocale
    模块:

    import _bootlocale
    
    old = _bootlocale.getpreferredencoding  # so you can restore
    _bootlocale.getpreferredencoding = lambda *args: 'UTF-8'
    
    这对我来说很有用(同样在OS X和CentOS 7上,使用3.6进行了测试):


    为什么不直接使用
    open('test.txt','r',encoding='utf8')
    ?显式比隐式好。不要使用黑客。
    sys.getfilesystemencoding()
    不用于确定新打开文件的编码。替换
    locale.getpreferredencoding
    不起作用,因为打开文件的C代码不会调用Python版本,它可以直接访问原始C函数。
    PYTHONIOENCODING
    适用于
    stdin
    stdout
    stderr
    ,而不是
    open()
    。为什么不在运行Python时使用
    LC_ALL='.UTF-8'
    ?您不需要设置整个区域设置,只要环境变量就足够了。@MartijnPieters我指出,添加
    encoding='utf8'
    选项对代码段有效。但它不适用于我导入的所有包,也不适用于在内部打开文件的所有包。我需要将
    LC_CTYPE
    设置为
    .UTF-8
    ,而不需要显式设置(因为Yocto Linux发行版默认情况下不安装区域设置)。我的问题是关于Intel Edison的。这是一个嵌入式系统,具有可定制的最小Linux系统,缺少macOS X和桌面Linux发行版所具有的许多功能。特别是,没有安装区域设置,因此设置
    LC.*
    只会导致错误消息:
    -sh:warning:setlocale:LC.\u ALL:无法更改区域设置(.UTF-8):没有这样的文件或目录
    @user8472:但这只是一个警告。Python工作吗?Python解释器的输出总是
    ANSI_X3.4-1968
    ,而不管
    LC_ALL
    环境变量的内容如何。同样,解释器中的
    open
    命令总是失败,除非我显式地将编码设置为
    'utf-8'
    。我包含的包中的
    open
    命令也会重复失败,而不管环境变量
    LC\u ALL
    的值是多少。@user8472:这太糟糕了。这仅仅表明,在打开文件时,您永远不应该依赖于区域设置,并且无论何时您想要读取非ASCII的文本数据,都必须使用
    encoding='…'
    。然而,我发现了
    io
    模块真正使用的钩子,请参阅更新。是的,这很有效!对于我的代码(我同意我应该显式指定编码)和导入的代码(我没有控制权,可能无法显式指定编码)。
    >>> 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'