有没有一种方法可以更改Python的open()函数默认的文本编码?

17

我能否以跨平台的方式更改默认的open()(2.7中使用io.open())文本编码?

这样我就不需要每次都指定open(...,encoding='utf-8')了。

在文本模式下,如果未指定编码,则使用的编码取决于平台:locale.getpreferredencoding(False)将被调用以获取当前区域设置编码。

虽然文档没有说明如何设置首选编码。该函数位于locale模块中,因此我需要更改区域设置吗?是否有可靠的跨平台方法来设置UTF-8区域设置?它会影响除默认文本文件编码之外的其他内容吗?

或者说更改区域设置是有风险的(可能会破坏某些东西),所以我应该坚持使用自定义包装器,例如:

def uopen(*args, **kwargs):
    return open(*args, encoding='UTF-8', **kwargs)
5个回答

20

不要更改语言环境或首选编码,因为:

  • 这可能会影响您的代码其他部分(或您正在使用的库);
  • 并且不清楚您的代码依赖于open使用特定的编码。

相反,请使用一个简单的包装器:

from functools import partial
open_utf8 = partial(open, encoding='UTF-8')

如果需要的话,您也可以为所有关键字参数指定默认值。


我尝试过 locale.setlocale(),但它没有改变Windows上的默认编码。即使是不同的非Unicode编码。所以我决定检查CPython的源代码,并发现getpreferredencoding使用了GetACP WinAPI函数,它“检索当前Windows ANSI代码页标识符”。 - user
除了使用版本相关的hack(如Joran在另一个答案中提出的建议以及在此问题的答案中找到的那些),Python中没有机制来覆盖这种行为。根据我所读的,除了在Python外部无法将此编码设置为UTF-8之外,在Windows中也没有机制可以设置此编码。因此,考虑到没有办法在不诉诸hack的情况下设置此首选项,我同意更改此设置可能是不可靠的。答案已被接受。 - user

2

您可以设置编码...但这真的很hacky(粗糙)。

import sys
sys.getdefaultencoding() #should print your default encoding
sys.setdefaultencoding("utf8") #error ... no setdefaultencoding ... but...
reload(sys)
sys.setdefaultencoding("utf8")  #now it succeeds ...

我会选择这样做:

main_script.py

import __builtin__
old_open = open
def uopen(*args, **kwargs):
    return open(*args, encoding='UTF-8', **kwargs)
__builtin__.open = uopen

无论在何处调用 open,它都将使用 utf8 编码... 但是如果您明确添加了编码,则可能会出现错误。

或者只需在打开文件时显式传递编码,或使用您的包装器...

Python 的一般哲学是显式比隐式更好,这意味着在打开文件时显式声明编码是“正确”的解决方案...


我不确定直接覆盖 builtins 中的函数是否安全,因为这也会修改已经导入模块中的函数,而某些库可能依赖于默认值。尽管如此,在某些情况下,这可能会很方便。谢谢,+1。 - user

1
也许 PEP 540 (UTF-8 模式) 是你想要的: https://peps.python.org/pep-0540/ 使用 -Xutf8
python.exe -Xutf8 -c "open('tmp.txt', 'w').write('天地玄黄0123'); print(open('tmp.txt').read())"

在PowerShell中使用PYTHONUTF8


$env:PYTHONUTF8=1; python.exe -c "open('tmp.txt', 'w').write('天地玄黄0123'); print(open('tmp.txt').read())"

Use PYTHONUTF8 in Cmd

set PYTHONUTF8=1&& python.exe -c "open('tmp.txt', 'w').write('天地玄黄0123'); print(open('tmp.txt').read())"

Use PYTHONUTF8 in Bash

PYTHONUTF8=1 python -c "open('tmp.txt', 'w').write('天地玄黄0123'); print(open('tmp.txt').read())"

您还可以执行setx PYTHONUTF8 1将其保存为用户级环境变量。

(意思是:您可以运行此命令将其保存为用户级别的环境变量,以便在以后使用时无需再次输入。)

这是我在Python 3中唯一有效的答案!虽然为了进行适当的测试,我认为您应该排除encoding ='utf8'参数以使其失败。否则,它可以成功地工作而无需设置环境变量。 - wisbucky

1
如果您确实需要更改默认编码,可以替换内置的open函数。
original_open = __builtins__.open
def uopen(*args, **kwargs):
    if "b" not in (args[1] if len(args) >= 2 else kwargs.get("mode", "")):
        kwargs.setdefault("encoding", "UTF-8")
    return original_open(*args, **kwargs)
__builtins__.open = uopen

在一封邮件列表中,我发现这些邮件讨论了如何替换print函数。于是我编写并测试了这段代码。


-1

我不会更改locale中的任何内容,因为这可能会在系统的其他部分产生很多副作用。open是一个系统级别的函数调用,因此它的设置可能会影响到其他Python程序或至少使用相同Python安装的其他程序。您的包装器看起来很合适,非常干净和可移植,并且似乎是正确的解决方案。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接