Python、Unicode与Windows控制台
当我在Windows的控制台里尝试用print
输出一个字符串时,有时候会出现一个错误,提示UnicodeEncodeError: 'charmap' codec can't encode character ....
。我猜这是因为Windows控制台无法处理所有的Unicode字符。
那我该怎么解决这个问题呢?比如,我怎么才能让程序在遇到这些字符时显示一个替代符号(比如?
),而不是直接出错呢?
15 个回答
更新: 在Python 3.6或更高版本中,直接在Windows控制台打印Unicode字符串是可以正常工作的。
所以,建议你升级到最新的Python版本,这样就没问题了。如果需要的话,我推荐使用2to3工具来把你的代码更新到Python 3.x,并且可以放弃对Python 2.x的支持。请注意,自2021年12月以来,Python 3.7之前的任何版本(包括Python 2.7)都没有安全支持,你可以查看这个链接了解更多信息。
如果你真的还需要支持早期版本的Python(包括Python 2.7),可以使用这个链接,它是基于之前提到的答案中的代码,并使用相同的API。(那个链接里有关于Windows字体配置的一些信息,但我怀疑这些信息在Windows 8或更高版本中是否仍然适用。)
注意:尽管有其他看起来合理的答案建议将代码页改为65001,但在Python 3.8之前,这个方法并没有效果。(从那时起它有点有效,但正如上面所说,对于Python 3.6及以上版本,你根本不需要这样做。)此外,使用sys.setdefaultencoding
来改变默认编码(仍然)不是个好主意。
更新: Python 3.6 实现了 PEP 528: 将Windows控制台编码改为UTF-8:现在Windows的默认控制台可以接受所有Unicode字符。 在内部,它使用与下面提到的win-unicode-console
包相同的Unicode API。现在,print(unicode_string)
应该可以正常工作了。
我遇到了一个
UnicodeEncodeError: 'charmap' codec can't encode character...
的错误。
这个错误的意思是你尝试打印的Unicode字符无法用当前的控制台字符编码(chcp
)表示。这个编码通常是8位编码,比如cp437
,它只能表示大约0x100个字符,而Unicode字符总共有大约1百万个:
>>> u"\N{EURO SIGN}".encode('cp437') Traceback (most recent call last): ... UnicodeEncodeError: 'charmap' codec can't encode character '\u20ac' in position 0: character maps to
我认为这是因为Windows控制台不接受仅Unicode字符。有什么好的解决办法吗?
其实Windows控制台是可以接受Unicode字符的,而且如果相应的字体配置正确,它甚至可以显示这些字符(仅限BMP)。建议使用WriteConsoleW()
API,具体可以参考@Daira Hopwood的回答。如果你使用win-unicode-console
包,你可以透明地调用这个API,也就是说,你不需要修改你的脚本:
T:\> py -m pip install win-unicode-console
T:\> py -m run your_script.py
可以查看Python 3.4、Unicode、不同语言和Windows之间的关系是什么?
有没有办法让我在这种情况下,Python自动打印一个
?
而不是出错?
如果在你的情况下,替换所有无法编码的字符为?
就足够了,那么你可以设置PYTHONIOENCODING
环境变量:
T:\> set PYTHONIOENCODING=:replace
T:\> python3 -c "print(u'[\N{EURO SIGN}]')"
[?]
在Python 3.6及以上版本中,除非设置了PYTHONLEGACYWINDOWSIOENCODING
环境变量为非空字符串,否则PYTHONIOENCODING
环境变量在交互式控制台缓冲区中会被忽略。
注意:这个回答有点过时(来自2008年)。请谨慎使用下面的解决方案!!
这里有一个页面详细说明了这个问题和解决方案(可以在页面上搜索 Wrapping sys.stdout into an instance 这段文字):
以下是该页面的一段代码摘录:
$ python -c 'import sys, codecs, locale; print sys.stdout.encoding; \
sys.stdout = codecs.getwriter(locale.getpreferredencoding())(sys.stdout); \
line = u"\u0411\n"; print type(line), len(line); \
sys.stdout.write(line); print line'
UTF-8
<type 'unicode'> 2
Б
Б
$ python -c 'import sys, codecs, locale; print sys.stdout.encoding; \
sys.stdout = codecs.getwriter(locale.getpreferredencoding())(sys.stdout); \
line = u"\u0411\n"; print type(line), len(line); \
sys.stdout.write(line); print line' | cat
None
<type 'unicode'> 2
Б
Б
页面上还有更多信息,非常值得一读。