更改 Python 的默认编码?

在从控制台运行应用程序时,巨蟒有许多“无法编码”和“无法解码”的问题。但是在 日食 PyDev IDE 中,默认的字符编码设置为 UTF-8,我没事。

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

那么最好的解决办法是什么呢?

367219 次浏览

PyDev3.4.1开始,不再更改默认编码。 详情请参阅 这张票

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

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

最后: 可以同时使用 答)B)或 < strong > !

这里有一个更简单的方法(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库中。)

但是,这样做并不安全: 这显然是一种黑客行为,因为在 Python 启动时,sys.setdefaultencoding()被故意从 sys中删除。重新启用它并更改默认编码 可以破解依赖于默认 ASCII 的代码(该代码可以是第三方的,这通常会使修复它变得不可能或危险)。

附注: 这种黑客技术似乎不再适用于 Python 3.9了。

如果在尝试管道/重定向脚本输出时出现此错误

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

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

export PYTHONIOENCODING=utf8

有一篇关于这个问题的博客文章很有见地。

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

我在下面解释它的内容。

在 python2中,对于字符串的编码没有那么强类型化,您可以对不同编码的字符串执行操作,并且成功。例如,下面将返回 True

u'Toshio' == 'Toshio'

对于在 sys.getdefaultencoding()中编码的每个(正常的,无前缀的)字符串,默认值为 ascii,但不包括其他字符串。

默认的编码应该在 site.py中在整个系统范围内进行更改,而不是在其他地方进行更改。在用户模块中设置它的 Hacks (也在这里提到)仅仅是 Hacks,而不是解决方案。

Python 3确实将系统编码默认改为 utf-8(当 LC _ CTYPE 支持 unicode 时) ,但是基本问题已经解决了,只要将“ byte”字符串与 unicode 字符串一起使用,就需要显式地对它们进行编码。

关于 python2(只有 python2) ,前面的一些答案依赖于使用以下黑客技巧:

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

不鼓励使用它(选中 这个这个)

在我的例子中,它有一个副作用: 我使用的是 ipython 笔记本,一旦我运行了代码,print 函数就不再工作了。我想这个问题可能有解决方案,但是我仍然认为使用黑客技术不应该是正确的选择。

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

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

import sys
sys.setdefaultencoding('UTF8')

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

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

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

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

我所知道的解决在 sys.stdout 上 print‘ ing unicode 字符串和 Beyond-ascii str’s (例如来自文本)编码问题的最佳解决方案是: 照顾一个 sys.stdout (类文件对象) ,它能够并且可选地容忍需求:

  • 如果由于某种原因 sys.stdout.encodingNone,或者不存在,或者错误地错误地错误地错误地错误地错误地错误地错误地错误地错误地错误地错误地错误地错误地错误地错误地错误地错误地错误地错误地错误地错误地错误地错误地错误地错误地错误地错误地错误。最后用一个类似文件的翻译对象代替 sys.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 的字符串文字写入代码,而不必总是被迫使用 u'string'风格的 unicode 转义。尽管 匿名獾的文章提到了这一点,但是这可以通过一致使用 ascii 或 UTF-8纯字符串的 Python 2或 Python 2 + 3源代码基础来实现——只要这些字符串可能进行静默 Unicode 转换并在模块之间移动或可能转到 stdout。对于这一点,更喜欢“ # encoding: utf-8”或 ascii (无声明)。更改或删除库,这些库仍然以一种非常愚蠢的方式致命地依赖于 chr # 127以外的 ascii 默认编码错误(这在今天是很少见的)。

在应用程序启动(和/或通过 sitecustomize.py )时,除了上面的 SmartStdout方案外,还要这样做——不要使用 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 转换,就好像只有 Python 3一样。 文件 I/O 当然总是需要特别注意编码——就像 Python 3中一样。

注意: 平原字符串然后在 SmartStdout中从 utf-8隐式转换为 unicode,然后再转换为输出流结束。

下面是我用来生成与 蟒蛇2蟒蛇3兼容并且总是生成 Utf8输出的代码的方法。我在别的地方找到了答案,但我记不起来来源了。

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

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

对于那些(1)在 Windows 平台上(2)运行 Python 2.7和(3)因为一个好的软件(例如,不是由你编写的,所以不会立即成为编码/解码打印操作的候选者)不能在 IDLE 环境中显示“漂亮的 Unicode 字符”(Python 打印的 unicode 很好)而感到恼火的人来说,这是一个快速的技巧。例如,Stephan Boyer 在他的 一阶逻辑证明程序教学论证的输出中使用的整洁的一阶逻辑符号。

我不喜欢强迫系统重新加载的想法,而且我无法让系统配合设置环境变量,比如 PYTHONIOENCOding (尝试直接使用 Windows 环境变量,也可以在网站包的 sitecustomize.py 中使用一行代码 = ‘ utf-8’)。

因此,如果你愿意通过黑客手段来获得成功,那么你可以访问你的 IDLE 目录,通常是: “ C: Python 27 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

换句话说,在使编码变量等于 Locale getdefaultlocale的“ 试试看”之后注释掉原来的代码行(因为这样会得到你不想要的 cp1252) ,然后强制将它加到“ utf-8”(通过添加行‘ 编码 = ‘ utf-8’,如图所示)。

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

这为我解决了问题。

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

您可以更改整个操作系统的编码

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

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

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

如果您只想在文件 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