更改 Python 的默认编码?

Changing default encoding of Python?

提问人:Ali Nadalizadeh 提问时间:2/17/2010 最后编辑:Peter MortensenAli Nadalizadeh 更新时间:10/16/2023 访问量:388499

问:

当我从控制台运行我的应用程序时,我遇到了许多“无法编码”和“无法解码”的 Python 问题。但是在 Eclipse PyDev IDE 中,默认字符编码设置为 UTF-8,我很好。

我四处搜索设置默认编码,人们说 Python 在启动时删除了函数,我们不能使用它。sys.setdefaultencoding

那么最好的解决方案是什么呢?

Python 编码 UTF-8 控制台

评论

1赞 djc 2/17/2010
请参阅博客文章 The Illusive setdefaultencoding
3赞 Att Righ 5/26/2017
The best solution is to learn to use encode and decode correctly instead of using hacks.这当然是 python2 的可能,但代价是始终记住这样做/始终使用您自己的接口。我的经验表明,当您编写要同时使用 python2 和 python3 的代码时,这会变得非常成问题。

答:

18赞 ChristopheD 2/17/2010 #1

PyDev 3.4.1 开始,不再更改默认编码。 有关详细信息,请参阅此票证。

对于早期版本,解决方案是确保 PyDev 不使用 UTF-8 作为默认编码运行。在 Eclipse 下,运行对话框设置(如果我没记错的话,“运行配置”);您可以在“通用”选项卡上选择默认编码。如果您希望“尽早”出现这些错误(换句话说:在您的 PyDev 环境中),请将其更改为 US-ASCII。另请参阅有关此解决方法的原始博客文章

评论

1赞 Sean 4/30/2011
谢谢克里斯。特别是考虑到上面 Mark T 的评论,你的回答对我来说似乎是最合适的。对于那些主要不是 Eclipse/PyDev 用户的人来说,我自己永远不会弄清楚这一点。
0赞 Tim Diggins 2/22/2012
我想全局更改它(而不是每次运行配置一次),但还没有弄清楚如何 - 问了一个单独的问题:stackoverflow.com/questions/9394277/......
53赞 lukmdo 10/26/2011 #2

A) 要控制 sys.getdefaultencoding() 输出:

python -c 'import sys; print(sys.getdefaultencoding())'

ascii

然后

echo "import sys; sys.setdefaultencoding('utf-16-be')" > sitecustomize.py

PYTHONPATH=".:$PYTHONPATH" python -c 'import sys; print(sys.getdefaultencoding())'

utf-16-be

你可以把你的 sitecustomize.py 放在你的 .PYTHONPATH

另外,您可能想尝试 reload(sys).setdefaultencoding by @EOL

B) 要控制 stdin.encoding 和 stdout.encoding,您需要设置 PYTHONIOENCODING

python -c 'import sys; print(sys.stdin.encoding, sys.stdout.encoding)'

ascii ascii

然后

PYTHONIOENCODING="utf-16-be" python -c 'import sys; 
print(sys.stdin.encoding, sys.stdout.encoding)'

utf-16-be utf-16-be

最后:您可以使用 A) 或 B)两者兼而有之!

评论

0赞 lukmdo 2/4/2015
(仅限 python2)单独但有趣的是在上面扩展,请参阅讨论from __future__ import unicode_literals
175赞 Eric O. Lebigot 7/13/2013 #3

这是一个更简单的方法(hack),它可以将已从中删除的函数还给您:setdefaultencoding()sys

import sys
# sys.setdefaultencoding() does not exist, here!
reload(sys)  # Reload does the trick!
sys.setdefaultencoding('UTF8')

(Python 3.4+ 的注意事项:在库中。reload()importlib

不过,这并不是一件安全的事情:这显然是一个黑客攻击,因为从Python启动时就被故意删除了。重新启用它并更改默认编码可能会破坏依赖于 ASCII 作为默认编码的代码(此代码可以是第三方代码,这通常会使修复它变得不可能或危险)。sys.setdefaultencoding()sys

PS:这个黑客似乎不再适用于Python 3.9。

评论

11赞 ibotty 8/10/2015
我投了反对票,因为这个答案对运行现有应用程序没有帮助(这是解释问题的一种方式),在编写/维护应用程序时是错误的,在编写库时是危险的。正确的方法是设置(或在应用程序中,检查它是否设置正确并中止并显示有意义的错误消息)。LC_CTYPE
1赞 ibotty 8/10/2015
好吧,它没有提到,一开始是一个黑客。除此之外,没有提及它们是危险的答案是没有帮助的。
1赞 ibotty 8/11/2015
@EOL你是对的。不过,它确实影响了 preferredencoding(在 python 2 和 3 中):LC_CTYPE=C python -c 'import locale; print( locale.getpreferredencoding())'
1赞 Marlon Abeykoon 6/7/2016
@user2394901 一直不鼓励使用 sys.setdefaultencoding()!!py3k 的编码是硬连线的“utf-8”,更改它会引发错误。
1赞 negstek 8/14/2022
即使在重新加载后:“sys”也没有属性“setdefaultencoding”
98赞 iman 11/22/2014 #4

如果在尝试通过管道/重定向脚本输出时收到此错误

UnicodeEncodeError:“ascii”编解码器无法对位置 0-5 中的字符进行编码:序号不在范围内 (128)

只需在控制台中导出,然后运行代码即可。PYTHONIOENCODING

export PYTHONIOENCODING=utf8

评论

3赞 Pryo 2/9/2015
这是唯一对我有任何影响的解决方案。- 我在 Debian 7 上,语言环境设置损坏。谢谢。
4赞 ibotty 6/17/2015
设置为明智的内容。它也使所有其他程序都感到高兴。LC_CTYPE
7赞 Tino 9/28/2015
Python3 中一个更大的错误是,这不是默认设置。这使得脚本中断,只是因为PYTHONIOENCODING=utf8LC_ALL=C
0赞 Att Righ 5/26/2017
Set LC_CTYPE to something sensible instead这是一个合理的建议。当您尝试分发仅在另一个人的系统上工作的代码时,这并不那么有效。
0赞 Mingye Wang 3/14/2018
Debian 和 Redhat 操作系统使用语言环境来提供更明智的 C.glibc 上游正在努力添加它,所以也许我们不应该责怪 Python 尊重语言环境设置\...?C.utf8
8赞 ibotty 6/17/2015 #5

有一篇关于它的有见地的博客文章。

请参见 https://anonbadger.wordpress.com/2015/06/16/why-sys-setdefaultencoding-will-break-code/

我在下面解释它的内容。

在 python 2 中,关于字符串编码的类型不那么强,您可以对不同编码的字符串执行操作并成功。例如,以下内容将返回 。True

u'Toshio' == 'Toshio'

这将适用于以 中编码的每个(正常的、无前缀的)字符串,该字符串默认为 ,但不适用于其他字符串。sys.getdefaultencoding()ascii

默认编码应该在 中在整个系统范围内更改,而不是在其他地方更改。在用户模块中设置它的黑客(也在这里介绍)就是这样:黑客,而不是解决方案。site.py

Python 3 确实将系统编码更改为默认为 utf-8(当 LC_CTYPE 可识别 unicode 时),但基本问题得到了解决,即每当它们与 unicode 字符串一起使用时,都需要显式编码“字节”字符串。

13赞 kiril 9/16/2016 #6

关于 python2(仅限 python2),前一些答案依赖于使用以下 hack:

import sys
reload(sys)  # Reload is a hack
sys.setdefaultencoding('UTF8')

不鼓励使用它(检查这个或这个 )

就我而言,它有一个副作用:我正在使用 ipython 笔记本,一旦我运行代码,“print”函数就不再起作用。我想会有解决方案,但我仍然认为使用黑客不应该是正确的选择。

在尝试了许多选项之后,对我有用的选项是在 sitecustomize.py 中使用相同的代码,即该段代码的本意所在。评估该模块后,将从 sys 中删除 setdefaultencoding 函数。

所以解决方案是将代码附加到文件中:/usr/lib/python2.7/sitecustomize.py

import sys
sys.setdefaultencoding('UTF8')

当我使用 virtualenvwrapper 时,我编辑的文件是 .~/.virtualenvs/venv-name/lib/python2.7/sitecustomize.py

当我与 python 笔记本和 conda 一起使用时,它是~/anaconda2/lib/python2.7/sitecustomize.py

5赞 kxr 2/10/2017 #7

首先:仅仅根据输出终端流的需要设置一些随机的默认编码是不好的做法。 经常根据环境更改系统中已经到位的东西 - 例如.sys.stdin/stdout streams,sys.excepthook等。reload(sys)reload

解决 stdout 上的编码问题

我所知道的解决 sys.stdout 上“ing unicode strings and beyond-ascii ”(例如来自文字)的编码问题的最佳解决方案是:处理一个 sys.stdout(类文件对象),该对象能够并可选择地容忍以下需求:printstr

  • 如果由于某种原因,或不存在,或错误地错误或“小于”stdout终端或流的实际能力,则尝试提供正确的属性。最后,通过替换为一个翻译的类似文件的对象。sys.stdout.encodingNone.encodingsys.stdout & sys.stderr

  • 当终端/流仍然无法对所有出现的 unicode 字符进行编码时,并且当您不想因此而中断时,您可以在翻译类似文件的对象中引入编码替换行为。print

下面是一个示例:

#!/usr/bin/env python
# encoding: utf-8
import sys

class SmartStdout:
    def __init__(self, encoding=None, org_stdout=None):
        if org_stdout is None:
            org_stdout = getattr(sys.stdout, 'org_stdout', sys.stdout)
        self.org_stdout = org_stdout
        self.encoding = encoding or \
                        getattr(org_stdout, 'encoding', None) or 'utf-8'
    def write(self, s):
        self.org_stdout.write(s.encode(self.encoding, 'backslashreplace'))
    def __getattr__(self, name):
        return getattr(self.org_stdout, name)

if __name__ == '__main__':
    if sys.stdout.isatty():
        sys.stdout = sys.stderr = SmartStdout()

    us = u'aouäöüфżß²'
    print us
    sys.stdout.flush()

在 Python 2 / 2 + 3 代码中使用 beyond-ascii 纯字符串文字

我认为更改全局默认编码(仅为 UTF-8)的唯一充分理由是关于应用程序源代码决策 - 而不是因为 I/O 流编码问题:用于将超出 ascii 字符串文本写入代码,而不会被迫始终使用样式 unicode 转义。这可以通过处理 Python 2 或 Python 2 + 3 源代码基础来相当一致地完成(尽管 anonbadger 的文章是这么说的),该源代码基础始终如一地使用 ascii 或 UTF-8 纯字符串文字 - 只要这些字符串可能会经历静默 unicode 转换并在模块之间移动或可能转到 stdout。为此,最好使用 “” 或 ascii(无声明)。更改或删除库,这些库仍然以一种非常愚蠢的方式致命地依赖于 chr #127 之外的 ascii 默认编码错误(这在今天很少见)。u'string'# encoding: utf-8

除了上述方案之外,在应用程序启动时(和/或通过 sitecustomize.py)也这样做 - 无需使用:SmartStdoutreload(sys)

...
def set_defaultencoding_globally(encoding='utf-8'):
    assert sys.getdefaultencoding() in ('ascii', 'mbcs', encoding)
    import imp
    _sys_org = imp.load_dynamic('_sys_org', 'sys')
    _sys_org.setdefaultencoding(encoding)

if __name__ == '__main__':
    sys.stdout = sys.stderr = SmartStdout()
    set_defaultencoding_globally('utf-8') 
    s = 'aouäöüфżß²'
    print s

这样,字符串文字和大多数操作(字符迭代除外)都可以舒适地工作,而无需考虑 unicode 转换,就好像只有 Python3 一样。 当然,文件 I/O 总是需要特别注意编码 - 就像在 Python3 中一样。

注意:然后,普通字符串在转换为输出流编码之前从 utf-8 隐式转换为 unicode in。SmartStdout

6赞 Att Righ 5/26/2017 #8

这是我用来生成与 python2python3 兼容的代码的方法,并且总是生成 utf8 输出。我在别处找到了这个答案,但我不记得来源了。

这种方法的工作原理是替换为不太像文件的东西(但仍然只使用标准库中的东西)。这很可能会给底层库带来问题,但在简单的情况下,你可以很好地控制如何通过框架使用 sys.stdout out,这可能是一个合理的方法。sys.stdout

sys.stdout = io.open(sys.stdout.fileno(), 'w', encoding='utf8')
2赞 Dalton Bentley 6/6/2017 #9

对于以下任何人来说,这是一个快速的黑客 (1) 在 Windows 平台上 (2) 运行 Python 2.7 和 (3) 恼火,因为一个不错的软件(即,不是由您编写的,所以不会立即成为编码/解码打印操作的候选者)不会在 IDLE 环境中显示“漂亮的 unicode 字符”(Pythonwin 打印 unicode 很好), 例如,斯蒂芬·博耶(Stephan Boyer)在一阶逻辑证明者的教学证明的输出中使用的简洁的一阶逻辑符号。

我不喜欢强制系统重新加载的想法,我无法让系统配合设置环境变量,例如 PYTHONIOENCODING(尝试直接使用 Windows 环境变量并将其作为单行 ='utf-8') 在 site-packages 中的 sitecustomize.py 中删除)。

因此,如果您愿意尝试成功,请转到您的 IDLE 目录,通常: “C:\Python27\Lib\idlelib” 找到文件 IOBinding.py。复制该文件并将其存储在其他位置,以便您可以在选择时恢复为原始行为。使用编辑器(例如 IDLE)在 idlelib 中打开文件。转到此代码区域:

# Encoding for file names
filesystemencoding = sys.getfilesystemencoding()

encoding = "ascii"
if sys.platform == 'win32':
    # On Windows, we could use "mbcs". However, to give the user
    # a portable encoding name, we need to find the code page 
    try:
        # --> 6/5/17 hack to force IDLE to display utf-8 rather than cp1252
        # --> encoding = locale.getdefaultlocale()[1]
        encoding = 'utf-8'
        codecs.lookup(encoding)
    except LookupError:
        pass

In other words, comment out the original code line following the 'try' that was making the encoding variable equal to locale.getdefaultlocale (because that will give you cp1252 which you don't want) and instead brute force it to 'utf-8' (by adding the line 'encoding = 'utf-8' as shown).

I believe this only affects IDLE display to stdout and not the encoding used for file names etc. (that is obtained in the filesystemencoding prior). If you have a problem with any other code you run in IDLE later, just replace the IOBinding.py file with the original unmodified file.

-1赞 twasbrillig 4/13/2018 #10

This fixed the issue for me.

import os
os.environ["PYTHONIOENCODING"] = "utf-8"

评论

1赞 Eric H. 8/20/2020
Did not for me. But worked when exported the variable in the shell before entering python, or used reload(sys); sys.defaultencoding("utf-8").
1赞 user3064538 2/18/2020 #11

You could change the encoding of your entire operating system. On Ubuntu you can do this with

sudo apt install locales 
sudo locale-gen en_US en_US.UTF-8    
sudo dpkg-reconfigure locales
0赞 Oleksandr Tsurika 4/30/2021 #12

set default encoding of OS to be . Eg., on ubuntu edit file and set:UTF-8/etc/default/locale

LANG=en_US.UTF-8
LANGUAGE=en_US.UTF-8
LC_ALL=en_US.UTF-8
0赞 BaiJiFeiLong 1/8/2022 #13

If you only want stable support on file / without same declarations everywhere, here are two solutions:UTF-8readwrite

1. Patch io module at runtime (danger operation at your own risk)

import pathlib as pathlib
import tempfile

import chardet


def patchIOWithUtf8Default():
    import builtins
    import importlib.util
    import sys
    spec = importlib.util.find_spec("io")
    module = importlib.util.module_from_spec(spec)
    exec(compile(spec.loader.get_source(spec.name) + """
    def open(*args, **kwargs):
        args = list(args)
        mode = kwargs.get('mode', (args + [''])[1])
        if (len(args) < 4 and 'b' not in mode) or 'encoding' in kwargs:
            kwargs['encoding'] = 'utf8'
        elif len(args) >= 4 and args[3] is None:
            args[3] = 'utf8'
        return _io.open(*args, **kwargs)
    """, module.__spec__.origin, "exec"), module.__dict__)
    sys.modules[module.__name__] = module
    builtins.open = __import__("io").open
    importlib.reload(importlib.import_module("pathlib"))


def main():
    patchIOWithUtf8Default()
    filename = tempfile.mktemp()
    text = "Common\n常\nSense\n识\n天地玄黄"
    print("Original text:", repr(text))
    pathlib.Path(filename).write_text(text)
    encoding = chardet.detect(open(filename, mode="rb").read())["encoding"]
    print("Written encoding by pathlib:", encoding)
    print("Written text by pathlib:", repr(open(filename, newline="", encoding=encoding).read()))


if __name__ == '__main__':
    main()

Sample output:

Original text: 'Common\n常\nSense\n识\n天地玄黄'
Written encoding by pathlib: utf-8
Written text by pathlib: 'Common\r\n常\r\nSense\r\n识\r\n天地玄黄'

2. Use 3rd library as pathlib wrapper

https://github.com/baijifeilong/IceSpringPathLib

pip install IceSpringPathLib

import pathlib
import tempfile

import chardet

import IceSpringPathLib

tempfile.mktemp()
filename = tempfile.mktemp()
text = "Common\n常\nSense\n识\n天地玄黄"
print("Original text:", repr(text))

pathlib.Path(filename).write_text(text)
encoding = chardet.detect(open(filename, mode="rb").read())["encoding"]
print("\nWritten text by pathlib:", repr(open(filename, newline="", encoding=encoding).read()))
print("Written encoding by pathlib:", encoding)

IceSpringPathLib.Path(filename).write_text(text)
encoding = chardet.detect(open(filename, mode="rb").read())["encoding"]
print("\nWritten text by IceSpringPathLib:", repr(open(filename, newline="", encoding=encoding).read()))
print("Written encoding by IceSpringPathLib:", encoding)

Sample output:

Original text: 'Common\n常\nSense\n识\n天地玄黄'

Written text by pathlib: 'Common\r\n常\r\nSense\r\n识\r\n天地玄黄'
Written encoding by pathlib: GB2312

Written text by IceSpringPathLib: 'Common\n常\nSense\n识\n天地玄黄'
Written encoding by IceSpringPathLib: utf-8
1赞 walkman 3/21/2023 #14

windows set environment variable PYTHONUTF8=1