ChatGPT解决这个技术问题 Extra ChatGPT

更改 Python 的默认编码?

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

我四处寻找设置默认编码,有人说Python在启动时删除了sys.setdefaultencoding功能,我们不能使用它。

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

请参阅博文 The Illusive setdefaultencoding
The best solution is to learn to use encode and decode correctly instead of using hacks. 这当然可以通过 python2 实现,但代价是始终记得这样做/始终使用您自己的界面。我的经验表明,当您编写要同时使用 python2 和 python3 的代码时,这会变得非常有问题。

E
Eric O Lebigot

这是一个更简单的方法 (hack),它可以返回从 sys 中删除的 setdefaultencoding() 函数:

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

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

这样做并不安全:这显然是一种 hack,因为当 Python 启动时,sys.setdefaultencoding() 被故意从 sys 中删除。重新启用它并更改默认编码 can break code that relies on ASCII being the default(此代码可以是第三方代码,这通常会使修复它变得不可能或很危险)。


我投了反对票,因为该答案无助于运行现有应用程序(这是解释问题的一种方式),在您编写/维护应用程序时是错误的,在编写库时是危险的。正确的方法是设置 LC_CTYPE (或在应用程序中,检查它是否设置正确并以有意义的错误消息中止)。
@ibotty我同意这个答案是一个黑客并且使用它很危险。不过,它确实回答了这个问题(“更改 Python 的默认编码?”)。你有关于环境变量 LC_CTYPE 对 Python 解释器的影响的参考吗?
好吧,它没有提到,起初它是一个黑客。除此之外,没有提及它们的危险答案没有帮助。
@EOL你是对的。它确实会影响首选编码(在 python 2 和 3 中):LC_CTYPE=C python -c 'import locale; print( locale.getpreferredencoding())'
@user2394901 一直不鼓励使用 sys.setdefaultencoding() !并且 py3k 的编码被硬连接到“utf-8”并且更改它会引发错误。
i
iman

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

UnicodeEncodeError: 'ascii' codec can't encode characters in position 0-5: ordinal not in range(128)

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

export PYTHONIOENCODING=utf8


这是唯一对我有任何影响的解决方案。 - 我在 Debian 7 上,语言环境设置损坏。谢谢。
LC_CTYPE 设置为合理的值。它也让所有其他程序都很开心。
Python3 中一个更大的错误是,PYTHONIOENCODING=utf8 不是默认值。这使得脚本中断只是因为 LC_ALL=C
Set LC_CTYPE to something sensible instead 这是一个合理的建议。当您尝试在其他人的系统上分发正常工作的代码时,这不会很好地工作。
Debian 和 Redhat 操作系统使用 C.utf8 语言环境来提供更合理的 C。glibc 上游正在努力添加它,所以也许我们不应该责怪 Python 尊重语言环境设置\...?
C
Community

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 中更高的位置。

你也可以试试@EOL 的reload(sys).setdefaultencoding

B) 要控制 stdin.encodingstdout.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) 或两者都使用!


(仅限 python2)单独但有趣的是在上面扩展 from __future__ import unicode_literals 参见 discussion
P
Peter Mortensen

PyDev 3.4.1 开始,不再更改默认编码。详见this ticket

对于早期版本,一个解决方案是确保 PyDev 不以 UTF-8 作为默认编码运行。在 Eclipse 下,运行对话框设置(“运行配置”,如果我没记错的话);您可以在常用选项卡上选择默认编码。如果您想“尽早”出现这些错误(换句话说:在您的 PyDev 环境中),请将其更改为 US-ASCII。另见an original blog post for this workaround


谢谢克里斯。特别是考虑到上面 Mark T 的评论,你的回答似乎对我来说是最合适的。对于主要不是 Eclipse/PyDev 用户的人来说,我自己永远也想不通。
我想全局更改它(而不是每次运行配置一次),但还没有弄清楚如何 - 已经问了一个单独的 q:stackoverflow.com/questions/9394277/…
C
Community

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

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

不鼓励使用它(检查 thisthis

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

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

因此解决方案是将代码附加到文件 /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


i
ibotty

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

请参阅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 字符串一起使用时显式编码“字节”字符串的要求得到解决。


k
kxr

首先:reload(sys) 并根据输出终端流的需要设置一些随机默认编码是不好的做法。 reload 经常根据环境更改 sys 中已放置的内容 - 例如 sys.stdin/stdout 流、sys.excepthook 等。

解决标准输出上的编码问题

我知道解决 sys.stdout 上 print'ing unicode 字符串和超出 ascii str's(例如来自文字)的编码问题的最佳解决方案是:照顾 sys.stdout(类似文件object) 有能力并且可以选择性地容忍以下需求:

当 sys.stdout.encoding 由于某种原因为 None、不存在、错误地为假或“小于”标准输出终端或流的实际能力时,请尝试提供正确的 .encoding 属性。最后通过翻译文件类对象替换 sys.stdout 和 sys.stderr。

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

这里有一个例子:

#!/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 代码中使用超出 ascii 的纯字符串文字

我认为更改全局默认编码(仅限 UTF-8)的唯一充分理由是关于应用程序 source code 的决定 - 而不是因为 I/O 流编码问题: For write beyond-ascii将字符串文字转换为代码,而不必总是使用 u'string' 样式的 unicode 转义。这可以通过处理一致地使用 ascii 或 UTF-8 纯字符串文字的 Python 2 或 Python 2 + 3 源代码基础来相当一致地完成(尽管 anonbadger 的文章说) - 如只要这些字符串可能会进行无声的 Unicode 转换并在模块之间移动或可能转到标准输出。为此,首选“# encoding: utf-8”或 ascii(无声明)。更改或删除仍然以非常愚蠢的方式致命地依赖 chr #127 以外的 ascii 默认编码错误的库(这在今天很少见)。

除了上面的 SmartStdout 方案之外,在应用程序启动时(和/或通过 sitecustomize.py)执行此操作 - 不使用 reload(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 中一样。

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


A
Att Righ

这是我用来生成与 python2 和 python3 兼容的代码并且始终生成 utf8 输出的方法。我在其他地方找到了这个答案,但我不记得出处了。

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

sys.stdout = io.open(sys.stdout.fileno(), 'w', encoding='utf8')

D
Dalton Bentley

对于以下任何人来说,这是一个快速破解方法:(1) 在 Windows 平台上 (2) 运行 Python 2.7 和 (3) 因为一个不错的软件(即,不是由您编写,因此不能立即成为编码/解码打印的候选者)而烦恼操作)不会在 IDLE 环境中显示“漂亮的 unicode 字符”(Pythonwin 打印 unicode 很好),例如,Stephan Boyer 在 First Order Logic Prover 的教学证明器的输出中使用的整洁的一阶逻辑符号。

我不喜欢强制重新加载系统的想法,而且我无法让系统与设置环境变量(如 PYTHONIOENCODING)合作(尝试直接 Windows 环境变量并将其作为一个放在 site-packages 中的 sitecustomize.py 中)班轮='utf-8')。

因此,如果您愿意破解成功之路,请转到您的 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

换句话说,注释掉'try'后面的原始代码行,它使编码变量等于locale.getdefaultlocale(因为这会给你不想要的cp1252),而是强制它为'utf-8 ' (通过添加行 'encoding = 'utf-8' 如图所示)。

我相信这只会影响到标准输出的 IDLE 显示,而不影响用于文件名等的编码(这是在之前的 filesystemencoding 中获得的)。如果您稍后在 IDLE 中运行的任何其他代码有问题,只需将 IOBinding.py 文件替换为原始未修改的文件即可。


B
Boris Verkhovskiy

您可以更改整个操作系统的编码。 On Ubuntu you can do this

sudo apt install locales 
sudo locale-gen en_US en_US.UTF-8    
sudo dpkg-reconfigure locales

t
twasbrillig

这为我解决了这个问题。

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

不适合我。但是在进入python之前在shell中导出变量时工作,或者使用reload(sys); sys.defaultencoding("utf-8")。
O
Oleksandr Tsurika

将 OS 的默认编码设置为 UTF-8。例如,在 ubuntu 上编辑文件 /etc/default/locale 并设置:

LANG=en_US.UTF-8
LANGUAGE=en_US.UTF-8
LC_ALL=en_US.UTF-8

B
BaiJiFeiLong

如果您只想对文件 read/write 提供稳定的 UTF-8 支持,而不需要到处都使用相同的声明,这里有两种解决方案:

<强> 1。在运行时修补 io 模块(危险操作需您自担风险)

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()

样本输出:

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. 使用第三个库作为 pathlib 包装器

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)

样本输出:

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