Python、Unicode与Windows控制台

177 投票
15 回答
135664 浏览
提问于 2025-04-11 00:06

当我在Windows的控制台里尝试用print输出一个字符串时,有时候会出现一个错误,提示UnicodeEncodeError: 'charmap' codec can't encode character ....。我猜这是因为Windows控制台无法处理所有的Unicode字符。

那我该怎么解决这个问题呢?比如,我怎么才能让程序在遇到这些字符时显示一个替代符号(比如?),而不是直接出错呢?

15 个回答

31

更新: 在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来改变默认编码(仍然)不是个好主意

94

更新: 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环境变量在交互式控制台缓冲区中会被忽略。

38

注意:这个回答有点过时(来自2008年)。请谨慎使用下面的解决方案!!


这里有一个页面详细说明了这个问题和解决方案(可以在页面上搜索 Wrapping sys.stdout into an instance 这段文字):

PrintFails - Python Wiki

以下是该页面的一段代码摘录:

$ 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
  Б
  Б

页面上还有更多信息,非常值得一读。

撰写回答