改变 Python 的默认编码?
- 2024-12-04 08:56:00
- admin 原创
- 150
问题描述:
当我从控制台运行Python应用程序时,我遇到了许多“无法编码”和“无法解码”的问题。但在Eclipse PyDev IDE 中,默认字符编码设置为UTF-8,我没问题。
我到处搜索如何设置默认编码,有人说 Pythonsys.setdefaultencoding
在启动时会删除该函数,我们无法使用它。
那么最好的解决方案是什么?
解决方案 1:
这是一个更简单的方法(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 启动时sys.setdefaultencoding()
会故意将其删除sys
。重新启用它并更改默认编码可能会破坏依赖 ASCII 作为默认代码的代码(此代码可能是第三方的,这通常会使修复它变得不可能或危险)。
PS:这个 hack 似乎不再适用于 Python 3.9。
解决方案 2:
如果你在尝试管道/重定向脚本输出时收到此错误
UnicodeEncodeError:'ascii'编解码器无法对位置 0-5 的字符进行编码:序数不在范围内(128)
只需在控制台中导出PYTHONIOENCODING
,然后运行您的代码。
export PYTHONIOENCODING=utf8
解决方案 3:
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
@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)或两者!
解决方案 4:
从PyDev 3.4.1 开始,默认编码不再改变。有关详细信息,请参阅此票。
对于早期版本,解决方案是确保 PyDev 不以 UTF-8 作为默认编码运行。在 Eclipse 下,运行对话框设置(如果我没记错的话,是“运行配置”);您可以在公共选项卡上选择默认编码。如果您想“尽早”出现这些错误(换句话说:在您的 PyDev 环境中),请将其更改为 US-ASCII。另请参阅有关此解决方法的原始博客文章。
解决方案 5:
关于 python2 (仅限 python2),一些前面的答案依赖于使用以下 hack:
import sys
reload(sys) # Reload is a hack
sys.setdefaultencoding('UTF8')
不鼓励使用它(检查这个或这个)
就我而言,它有一个副作用:我使用的是 ipython 笔记本,一旦我运行代码,“print”函数就不再起作用了。我想应该有解决办法,但我仍然认为使用 hack 不是正确的选择。
尝试了许多选项之后,对我有用的选项是在 中使用相同的代码sitecustomize.py
,其中该代码段应该是。评估该模块后,setdefaultencoding 函数将从 sys 中删除。
因此解决方案是将/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
解决方案 6:
有一篇关于此的深刻博客文章。
请参阅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 字符串一起使用时明确编码“字节”字符串的要求得到解决。
解决方案 7:
这是我用来生成与python2和python3兼容且始终生成utf8输出的代码的方法。我在其他地方找到了这个答案,但我不记得来源了。
这种方法的工作原理是使用一些不太像文件的sys.stdout
东西来替换(但仍然只使用标准库中的东西)。这很可能会给您的底层库带来问题,但在您可以很好地控制 sys.stdout 如何在框架中使用的情况下,这可能是一种合理的方法。
sys.stdout = io.open(sys.stdout.fileno(), 'w', encoding='utf8')
解决方案 8:
首先:reload(sys)
仅仅为了输出终端流的需要而设置一些随机的默认编码是不好的做法。reload
通常会根据环境更改 sys 中已经设置的内容 - 例如 sys.stdin/stdout 流、sys.excepthook 等。
解决 stdout 上的编码问题
我所知道的解决sys.stdout 上的print
unicode 字符串和 beyond-ascii str
(例如来自文字)的编码问题的最佳解决方案是:处理一个 sys.stdout (类似文件的对象),它能够满足以下需求并且可以选择容忍:
当
sys.stdout.encoding
由于None
某种原因,或不存在,或错误地为假,或“小于”stdout终端或流的实际能力时,则尝试提供正确的.encoding
属性。最后通过替换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 \n 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 转义。这可以相当一致地完成(尽管anonbadger的文章这么说),通过处理一致使用 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 转换,就像只有 Python3 一样。当然,文件 I/O 总是需要特别注意编码 - 就像在 Python3 中一样。
SmartStdout
注意:在转换为输出流编码之前,纯文本字符串会被隐式地从 utf-8 转换为 unicode 。
解决方案 9:
这是一种快速破解方法,适用于以下任何人:(1) 在 Windows 平台上 (2) 运行 Python 2.7 并且 (3) 因为一个不错的软件(即不是你编写的,因此不是立即用于编码/解码打印操作的候选)不会显示“漂亮的 unicode 字符”在 IDLE 环境中(Pythonwin 可以很好地打印 unicode),例如,Stephan Boyer 在他的教学证明器(在First Order Logic Prover )的输出中使用的整洁的一阶逻辑符号。
我不喜欢强制重新加载系统的想法,而且我无法让系统配合设置环境变量,比如 PYTHONIOENCODING(尝试直接使用 Windows 环境变量,并将其放在站点包中的 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 向 stdout 的显示,而不会影响文件名等的编码(这是在之前的 filesystemencoding 中获得的)。如果您稍后在 IDLE 中运行的任何其他代码有问题,只需将 IOBinding.py 文件替换为原始未修改的文件即可。
解决方案 10:
你可以更改整个操作系统的编码。在 Ubuntu 上,你可以使用以下命令执行此操作
sudo apt install locales
sudo locale-gen en_US en_US.UTF-8
sudo dpkg-reconfigure locales
解决方案 11:
windows设置环境变量PYTHONUTF8=1
解决方案 12:
将操作系统的默认编码设置为UTF-8
。例如,在 ubuntu 上编辑文件/etc/default/locale
并设置:
LANG=en_US.UTF-8
LANGUAGE=en_US.UTF-8
LC_ALL=en_US.UTF-8
解决方案 13:
如果您只希望UTF-8
文件上有稳定的支持read
/write
不需要到处都有相同的声明,那么有两种解决方案:
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
常
Sense
识
天地玄黄"
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
常
Sense
识
天地玄黄'
Written encoding by pathlib: utf-8
Written text by pathlib: 'Common
常
Sense
识
天地玄黄'
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
常
Sense
识
天地玄黄"
print("Original text:", repr(text))
pathlib.Path(filename).write_text(text)
encoding = chardet.detect(open(filename, mode="rb").read())["encoding"]
print("
Written 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("
Written text by IceSpringPathLib:", repr(open(filename, newline="", encoding=encoding).read()))
print("Written encoding by IceSpringPathLib:", encoding)
示例输出:
Original text: 'Common
常
Sense
识
天地玄黄'
Written text by pathlib: 'Common
常
Sense
识
天地玄黄'
Written encoding by pathlib: GB2312
Written text by IceSpringPathLib: 'Common
常
Sense
识
天地玄黄'
Written encoding by IceSpringPathLib: utf-8
解决方案 14:
这为我解决了这个问题。
import os
os.environ["PYTHONIOENCODING"] = "utf-8"