提问人:Ali Nadalizadeh 提问时间:2/17/2010 最后编辑:Peter MortensenAli Nadalizadeh 更新时间:10/16/2023 访问量:388499
更改 Python 的默认编码?
Changing default encoding of Python?
问:
当我从控制台运行我的应用程序时,我遇到了许多“无法编码”和“无法解码”的 Python 问题。但是在 Eclipse PyDev IDE 中,默认字符编码设置为 UTF-8,我很好。
我四处搜索设置默认编码,人们说 Python 在启动时删除了函数,我们不能使用它。sys.setdefaultencoding
那么最好的解决方案是什么呢?
答:
从 PyDev 3.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
另外,您可能想尝试 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) 或两者兼而有之!
评论
这是一个更简单的方法(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。
评论
LC_CTYPE
LC_CTYPE=C python -c 'import locale; print( locale.getpreferredencoding())'
如果在尝试通过管道/重定向脚本输出时收到此错误
UnicodeEncodeError:“ascii”编解码器无法对位置 0-5 中的字符进行编码:序号不在范围内 (128)
只需在控制台中导出,然后运行代码即可。PYTHONIOENCODING
export PYTHONIOENCODING=utf8
评论
LC_CTYPE
PYTHONIOENCODING=utf8
LC_ALL=C
Set LC_CTYPE to something sensible instead
这是一个合理的建议。当您尝试分发仅在另一个人的系统上工作的代码时,这并不那么有效。
C.utf8
有一篇关于它的有见地的博客文章。
请参见 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 字符串一起使用时,都需要显式编码“字节”字符串。
关于 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
首先:仅仅根据输出终端流的需要设置一些随机的默认编码是不好的做法。 经常根据环境更改系统中已经到位的东西 - 例如.sys.stdin/stdout streams,sys.excepthook等。reload(sys)
reload
解决 stdout 上的编码问题
我所知道的解决 sys.stdout 上“ing unicode strings and beyond-ascii ”(例如来自文字)的编码问题的最佳解决方案是:处理一个 sys.stdout(类文件对象),该对象能够并可选择地容忍以下需求:print
str
如果由于某种原因,或不存在,或错误地错误或“小于”stdout终端或流的实际能力,则尝试提供正确的属性。最后,通过替换为一个翻译的类似文件的对象。
sys.stdout.encoding
None
.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 \
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)也这样做 - 无需使用: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 中一样。
注意:然后,普通字符串在转换为输出流编码之前从 utf-8 隐式转换为 unicode in。SmartStdout
这是我用来生成与 python2 和 python3 兼容的代码的方法,并且总是生成 utf8 输出。我在别处找到了这个答案,但我不记得来源了。
这种方法的工作原理是替换为不太像文件的东西(但仍然只使用标准库中的东西)。这很可能会给底层库带来问题,但在简单的情况下,你可以很好地控制如何通过框架使用 sys.stdout out,这可能是一个合理的方法。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)在一阶逻辑证明者的教学证明的输出中使用的简洁的一阶逻辑符号。
我不喜欢强制系统重新加载的想法,我无法让系统配合设置环境变量,例如 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.
This fixed the issue for me.
import os
os.environ["PYTHONIOENCODING"] = "utf-8"
评论
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
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
If you only want stable support on file / without same declarations everywhere, here are two solutions:UTF-8
read
write
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
windows set environment variable
PYTHONUTF8=1
评论
The best solution is to learn to use encode and decode correctly instead of using hacks.
这当然是 python2 的可能,但代价是始终记住这样做/始终使用您自己的接口。我的经验表明,当您编写要同时使用 python2 和 python3 的代码时,这会变得非常成问题。