更改Python的默认编码?

Changing default encoding of Python?

当我从控制台运行应用程序时,我有许多关于Python的"无法编码"和"无法解码"问题。但是在EclipsePydev IDE中,默认的字符编码设置为utf-8,我很好。

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

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


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

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

不过,这样做并不安全:这显然是一次黑客攻击,因为当python启动时,sys.setdefaultencoding()是有意从sys中删除的。重新启用它并更改默认编码可能会破坏依赖于ASCII作为默认值的代码(此代码可以是第三方代码,这通常会使修复变得不可能或危险)。


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

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

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

export PYTHONIOENCODING=utf8


a)控制sys.getdefaultencoding()输出:

1
python -c 'import sys; print(sys.getdefaultencoding())'

ascii

然后

1
echo"import sys; sys.setdefaultencoding('utf-16-be')"> sitecustomize.py

1
PYTHONPATH=".:$PYTHONPATH" python -c 'import sys; print(sys.getdefaultencoding())'

utf-16-be

你可以把你的sitecustomize.py放在你的PYTHONPATH中。

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

b)为了控制stdin.encodingstdout.encoding,您要设置PYTHONIOENCODING

1
python -c 'import sys; print(sys.stdin.encoding, sys.stdout.encoding)'

ascii ascii

然后

1
2
PYTHONIOENCODING="utf-16-be" python -c 'import sys;
print(sys.stdin.encoding, sys.stdout.encoding)'

utf-16-be utf-16-be

最后:您可以使用a)或b)或两者!


从pydev 3.4.1开始,默认编码不再被更改。详情请看这张票。

对于早期版本,解决方案是确保pydev不以utf-8作为默认编码运行。在Eclipse下,运行对话框设置("运行配置",如果我记得正确的话);您可以在公共选项卡上选择默认编码。如果您希望"早"出现这些错误(换句话说:在您的pydev环境中),请将其更改为us-ascii。还可以查看此解决方法的原始博客文章。


关于python2(仅限python2),以前的一些答案依赖于以下方法:

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

不鼓励使用它(检查这个或这个)

在我的例子中,它有一个副作用:我正在使用ipython笔记本,一旦我运行代码,"print"功能就不再有效。我想会有解决办法的,但我仍然认为使用黑客不应该是正确的选择。

在尝试了许多选择之后,对我有用的一个选择是在sitecustomize.py中使用相同的代码,而这段代码本来就应该是这样的。在评估该模块之后,setDefaultEncoding函数将从sys中删除。

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

1
2
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

1
u'Toshio' == 'Toshio'

对于在sys.getdefaultencoding()中编码的每一个(正常的、不固定的)字符串,该字符串默认为ascii,而不是其他字符串。

site.py中,默认编码应该在系统范围内进行更改,而不是在其他地方。在用户模块中设置它的黑客(也在这里介绍)只是:黑客,而不是解决方案。

python 3确实将系统编码改为默认的utf-8(当lc ctype支持Unicode时),但基本问题得到了解决,因为需要在与Unicode字符串一起使用时显式编码"byte"字符串。


这解决了我的问题。

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


首先:reload(sys)和设置一些随机默认编码只是为了满足输出终端流的需要,这是不好的做法。reload通常会根据环境改变已安装的sys中的内容,例如sys.stdin/stdout流、sys.excepthook等。

解决stdout上的编码问题

我所知道的解决e sys.stdout上的print的unicode字符串和超过ascii str的(例如,来自文本)的编码问题的最佳解决方案是:处理一个能够并且可选地容忍以下需求的sys.stdout(类似文件的对象):

    百万千克1

    sys.stdout.encoding由于某种原因是None时,或不存在,或错误地虚假或"小于"stdout终端或流的实际能力时,则尝试提供正确的.encoding属性。最后用翻译类文件对象替换sys.stdout & sys.stderr

    百万千克1百万千克1

    当终端/流仍然无法对所有发生的unicode字符进行编码时,并且当您不想仅仅因为这一点而破坏print时,可以在类似对象的翻译文件中引入encode with replace行为。

    百万千克1

这里有一个例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#!/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??üф??2'
    print us
    sys.stdout.flush()

在python 2/2+3代码中使用超越ASCII的纯字符串文本

我认为更改全局默认编码(仅适用于UTF-8)的唯一好理由是考虑应用程序源代码决策,而不是因为I/O流编码问题:将ASCII字符串以外的文字写入代码,而不必强制始终使用u'string'样式的Unicode转义。这可以相当一致地完成(尽管Anonbadger的文章中说了),通过处理python 2或python2+3源代码基础,它始终使用ascii或utf-8纯字符串文本-只要这些字符串可能进行无声的unicode转换并在模块之间移动,或者可能转到stdout。为此,首选"# encoding: utf-8或ascii(无声明)。更改或删除仍然以非常愚蠢的方式依赖于chr 127之外的ASCII默认编码错误(这在今天很少见)的库。

并且在应用程序启动时(和/或通过sitecustomize.py)这样做,除了上面的SmartStdout方案之外-不使用reload(sys)

1
2
3
4
5
6
7
8
9
10
11
12
...
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??üф??2'
    print s

这样,字符串文本和大多数操作(字符迭代除外)都可以在不考虑Unicode转换的情况下正常工作,就好像只有python3一样。文件I/O当然总是需要特别注意编码——就像python3一样。

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


对于那些(1)在运行python 2.7和(3)的Windows平台(2)上运行python的人来说,这是一个很快的黑客攻击,因为一个好的软件(即,不是由您编写的,所以不是立即进行编码/解码打印操作的候选软件)不会在空闲环境中显示"漂亮的unicode字符"(pythonwin打印unicode fine),例如,t他整洁的一阶逻辑符号,斯蒂芬博伊尔使用的输出从他的教学证明在一阶逻辑证明。

我不喜欢强制系统重新加载的想法,也无法让系统配合设置诸如pythonioencoding之类的环境变量(尝试了direct windows环境变量,并将其作为一个liner='utf-8')放到sitecustomize.py的站点包中。

因此,如果你愿意通过黑客手段获得成功,请转到空闲目录,通常是:"C:python27libidlelib"找到文件iobinding.py。制作该文件的副本并将其存储在其他地方,以便在选择时恢复到原始行为。用编辑器打开idlelib中的文件(例如idle)。转到此代码区域:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 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的"try"后面注释原始代码行(因为这样会给您提供不需要的cp1252),而将其强制为"utf-8"(如图所示添加"encoding='utf-8")。

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


下面是我用来生成与python2和python3兼容并始终生成utf8输出的代码的方法。我在别处找到了这个答案,但我记不清来源。

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

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

你想写西班牙语单词吗?恩蟒)

1
2
3
4
#!/usr/bin/env python
# -*- coding: iso-8859-15 -*-

print"Pi?a"