当我从控制台运行我的应用程序时,Python 有很多“无法编码”和“无法解码”的问题。但是在 Eclipse PyDev IDE 中,默认字符编码设置为 UTF-8,我很好。
我四处寻找设置默认编码,有人说Python在启动时删除了sys.setdefaultencoding
功能,我们不能使用它。
那么最好的解决方案是什么?
The best solution is to learn to use encode and decode correctly instead of using hacks.
这当然可以通过 python2 实现,但代价是始终记得这样做/始终使用您自己的界面。我的经验表明,当您编写要同时使用 python2 和 python3 的代码时,这会变得非常有问题。
这是一个更简单的方法 (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(此代码可以是第三方代码,这通常会使修复它变得不可能或很危险)。
如果您在尝试管道/重定向脚本的输出时收到此错误
UnicodeEncodeError: 'ascii' codec can't encode characters in position 0-5: ordinal not in range(128)
只需在控制台中导出 PYTHONIOENCODING,然后运行您的代码。
export PYTHONIOENCODING=utf8
LC_CTYPE
设置为合理的值。它也让所有其他程序都很开心。
PYTHONIOENCODING=utf8
不是默认值。这使得脚本中断只是因为 LC_ALL=C
Set LC_CTYPE to something sensible instead
这是一个合理的建议。当您尝试在其他人的系统上分发正常工作的代码时,这不会很好地工作。
C.utf8
语言环境来提供更合理的 C。glibc 上游正在努力添加它,所以也许我们不应该责怪 Python 尊重语言环境设置\...?
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.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) 或两者都使用!
from __future__ import unicode_literals
参见 discussion
从 PyDev 3.4.1 开始,不再更改默认编码。详见this ticket。
对于早期版本,一个解决方案是确保 PyDev 不以 UTF-8 作为默认编码运行。在 Eclipse 下,运行对话框设置(“运行配置”,如果我没记错的话);您可以在常用选项卡上选择默认编码。如果您想“尽早”出现这些错误(换句话说:在您的 PyDev 环境中),请将其更改为 US-ASCII。另见an original blog post for this workaround。
关于 python2(和仅 python2),以前的一些答案依赖于使用以下 hack:
import sys
reload(sys) # Reload is a hack
sys.setdefaultencoding('UTF8')
就我而言,它有一个副作用:我正在使用 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
有一篇关于它的有见地的博客文章。
请参阅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 字符串一起使用时显式编码“字节”字符串的要求得到解决。
首先: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。
这是我用来生成与 python2 和 python3 兼容的代码并且始终生成 utf8 输出的方法。我在其他地方找到了这个答案,但我不记得出处了。
这种方法通过将 sys.stdout
替换为 不太像文件 的东西(但仍然只使用标准库中的东西)来工作。这很可能会给您的底层库带来问题,但是在您可以很好地控制如何通过框架使用 sys.stdout 的简单情况下,这可能是一种合理的方法。
sys.stdout = io.open(sys.stdout.fileno(), 'w', encoding='utf8')
对于以下任何人来说,这是一个快速破解方法:(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 文件替换为原始未修改的文件即可。
您可以更改整个操作系统的编码。 On Ubuntu you can do this 与
sudo apt install locales
sudo locale-gen en_US en_US.UTF-8
sudo dpkg-reconfigure locales
这为我解决了这个问题。
import os
os.environ["PYTHONIOENCODING"] = "utf-8"
将 OS 的默认编码设置为 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
不定期副业成功案例分享
LC_CTYPE
(或在应用程序中,检查它是否设置正确并以有意义的错误消息中止)。LC_CTYPE=C python -c 'import locale; print( locale.getpreferredencoding())'