Python,Unicode和Windows控制台


145

当我尝试在Windows控制台中打印Unicode字符串时,出现UnicodeEncodeError: 'charmap' codec can't encode character ....错误。我认为这是因为Windows控制台不接受仅Unicode字符。最好的办法是什么?有什么方法可以使Python自动打印?而不是在这种情况下失败?

编辑: 我正在使用Python 2.5。


注意:带有对勾标记的@ LasseV.Karlsen答案有点过时(自2008年起)。请谨慎使用以下解决方案/答案/建议!

截至今天(2016年1月6日),@ JFSebastian的答案更有意义。


您正在使用什么版本的Python?我看过一些参考资料,指出它在2.4.3中已被破坏,并在2.4.4中已得到修复。
Stu


检查出。
Soorena

1
我发现的最简单答案是键入:chcp 65001,然后在cmd中使用pyhton
Soorena 2013年

1
然后,您应该更改您接受的答案...
Mr_and_Mrs_D

Answers:


38

注意:这个答案有点过时了(从2008年开始)。请谨慎使用以下解决方案!


这是一个详细说明问题和解决方案的页面(在该页面中将sys.stdout文本包装到实例中):

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

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


7
该链接已死,没有给出答案的要点。-1
0xC0000022L13年

1
当我尝试有关包装的建议时sys.stdout,它会打印错误的内容。例如,u'\u2013'变为û而不是一个破折号。
user2357112支持Monica 2014年

@ user2357112您将不得不对此发布一个新问题。Unicode和系统控制台不一定是最好的组合,但是对此我还不够了解,因此,如果您需要一个确定的答案,请在此处于SO上提问。
Lasse V. Karlsen 2014年

2
链接已死。对于Windows控制台,该代码示例是错误的,因为Windows控制台的代码页(OEM)cp437与Windows ANSI代码页(例如)不同cp1252。该代码无法修复UnicodeEncodeError: 'charmap' codec can't encode character错误,并且可能会导致mojibake,例如ا©被静默替换╪º⌐
jfs

73

更新: Python 3.6实现了PEP 528:将Windows控制台编码更改为UTF-8Windows上的默认控制台现在将接受所有Unicode字符。在内部,它使用与下面提到win-unicode-console相同的Unicode API 。print(unicode_string)应该现在就可以工作。


我得到一个UnicodeEncodeError: 'charmap' codec can't encode character... 错误。

该错误意味着您尝试打印的Unicode字符无法使用当前(chcp)控制台字符编码表示。代码页通常是8位编码,例如cp437只能表示1M Unicode字符中的〜0x100个字符:

>>> u“ \ N {EURO SIGN}”。encode('cp437')
追溯(最近一次通话):
...
UnicodeEncodeError:'charmap'编解码器无法在位置0编码字符'\ u20ac':
字符映射到 

我认为这是因为Windows控制台不接受仅Unicode字符。最好的办法是什么?

Windows控制台确实接受Unicode字符,如果配置了相应的字体,它甚至可以显示它们(仅BMP)。WriteConsoleW()应该按照@Daira Hopwood的答案中的建议使用API 。可以透明地调用它,即,如果您使用win-unicode-consolepackage,则不需要也不应修改脚本:

T:\> py -mpip install win-unicode-console
T:\> py -mrun your_script.py

请参阅对Python 3.4,Unicode,不同的语言和Windows有何处理?

有什么方法可以使Python自动打印?而不是在这种情况下失败?

如果足以替换所有无法编码的字符,?则可以设置PYTHONIOENCODINGenvvar

T:\> set PYTHONIOENCODING=:replace
T:\> python3 -c "print(u'[\N{EURO SIGN}]')"
[?]

在Python 3.6+中,PYTHONIOENCODING除非将PYTHONLEGACYWINDOWSIOENCODINGenvvar设置为非空字符串,否则交互式控制台缓冲区将忽略envvar 指定的编码。


3
“ Windows上的默认控制台现在将接受所有Unicode字符”,您需要配置控制台:右键单击窗口顶部(cmd或python IDLE),以默认/字体选择“ Lucida控制台”。(日语和中文对我没有用,但没有它我应该活下来……)
JinSnow

2
@Guillaume:答案包含有关Windows控制台的粗体短语:“如果配置了相应的字体”。这个答案没有提到IDLE,但是您不需要在其中配置字体(默认情况下print('\u4E01'),我在IDLE中看到日文和中文字符就可以了。尝试使用,print('\u6b63'))。
jfs

2
@Guillaume如果在Windows 10中安装语言包,您甚至可以获得中文。它添加了支持中文的控制台字体。
Mark Tolonen


12

如果您对获取不良字符的可靠表示不感兴趣,则可以使用以下方式(使用python> = 2.6,包括3.x):

from __future__ import print_function
import sys

def safeprint(s):
    try:
        print(s)
    except UnicodeEncodeError:
        if sys.version_info >= (3,):
            print(s.encode('utf8').decode(sys.stdout.encoding))
        else:
            print(s.encode('utf8'))

safeprint(u"\N{EM DASH}")

字符串中的错误字符将转换为Windows控制台可打印的表示形式。


.encode('utf8').decode(sys.stdout.encoding)导致mojibake例如u"\N{EM DASH}".encode('utf-8').decode('cp437')->ΓÇö
jfs

只是print(s.encode('utf-8'))避免编译器错误的更好方法。相反,您得到\ xNN输出的不可打印字符,这足以满足我的诊断消息。
CODE-READ

4
这是极其严重的错误。编码为UTF-8然后解码为8位字符集将a)经常失败,并非所有代码页都具有针对所有256字节值的字符,并且b)总是对数据进行错误的解释,从而产生Mojibake混乱。
马丁·皮特斯

10

以下代码即使在Windows上也可以将Python输出作为UTF-8控制台输出。

控制台将在Windows 7上很好地显示字符,但是在Windows XP上将不会很好地显示字符,但是至少它可以正常工作,最重要的是,您将在所有平台上从脚本获得一致的输出。您将能够将输出重定向到文件。

以下代码已在Windows上使用Python 2.6进行了测试。


#!/usr/bin/python
# -*- coding: UTF-8 -*-

import codecs, sys

reload(sys)
sys.setdefaultencoding('utf-8')

print sys.getdefaultencoding()

if sys.platform == 'win32':
    try:
        import win32console 
    except:
        print "Python Win32 Extensions module is required.\n You can download it from https://sourceforge.net/projects/pywin32/ (x86 and x64 builds are available)\n"
        exit(-1)
    # win32console implementation  of SetConsoleCP does not return a value
    # CP_UTF8 = 65001
    win32console.SetConsoleCP(65001)
    if (win32console.GetConsoleCP() != 65001):
        raise Exception ("Cannot set console codepage to 65001 (UTF-8)")
    win32console.SetConsoleOutputCP(65001)
    if (win32console.GetConsoleOutputCP() != 65001):
        raise Exception ("Cannot set console output codepage to 65001 (UTF-8)")

#import sys, codecs
sys.stdout = codecs.getwriter('utf8')(sys.stdout)
sys.stderr = codecs.getwriter('utf8')(sys.stderr)

print "This is an Е乂αmp١ȅ testing Unicode support using Arabic, Latin, Cyrillic, Greek, Hebrew and CJK code points.\n"

1
是否可以通过仅使用其他控制台来避免这种情况?
endlith 2011年

@sorin:为什么先import win32console在a之外try,然后有条件地在a内try呢?并不是毫无意义的(第一个import
0xC0000022L13年

值得一提的是,David-Sarah Hopwood提供的软件有效(我没
让它

4
不要更改系统默认编码;改改您的Unicode值。更改默认编码会破坏依赖于默认行为的库。原因是必须先强制​​模块重新加载才能执行此操作。
马丁·彼得斯

7

只需在执行python脚本之前在命令行中输入以下代码即可:

chcp 65001 & set PYTHONIOENCODING=utf-8

5

就像GiampaoloRodolà的回答一样,但更加肮脏:我真的很想花很长时间(很快)来理解编码的整个主题以及它们如何应用于Windoze控制台,

就目前而言,我只想要sthg,这意味着我的程序不会崩溃,而且我了解...而且也没有涉及导入太多的外来模块(特别是我正在使用Jython,所以一半的时间是Python模块实际上并不可用)。

def pr(s):
    try:
        print(s)
    except UnicodeEncodeError:
        for c in s:
            try:
                print( c, end='')
            except UnicodeEncodeError:
                print( '?', end='')

注意:“ pr”的键入比“ print”的键入短(并且比“ safeprint”的键入要短很多)...!


聪明,一种快速而又肮脏的方式解决此问题。我认为这对于间歇性解决方案非常有用。
JFA

3

对于Python 2,请尝试:

print unicode(string, 'unicode-escape')

对于Python 3,请尝试:

import os
string = "002 Could've Would've Should've"
os.system('echo ' + string)

或者尝试使用win-unicode-console:

pip install win-unicode-console
py -mrun your_script.py

2

TL; DR:

print(yourstring.encode('ascii','replace'));

我自己遇到了这个问题,正在使用Twitch聊天(IRC)机器人。(最新的Python 2.7)

我想解析聊天消息以便回复...

msg = s.recv(1024).decode("utf-8")

还要以易于阅读的格式将它们安全地打印到控制台:

print(msg.encode('ascii','replace'));

这样就纠正了漫游器引发UnicodeEncodeError: 'charmap'错误的问题,并用替换了Unicode字符?


2

您的问题的原因不是 Win控制台不愿意接受Unicode(因为这样做是因为我猜默认是Win2k)。它是默认的系统编码。试试下面的代码,看看它能为您带来什么:

import sys
sys.getdefaultencoding()

如果显示ascii,则是由您引起的;-)您必须创建一个名为sitecustomize.py的文件,并将其放在python路径下(我将其放在/usr/lib/python2.5/site-packages下,但在获胜-它是c:\ python \ lib \ site-packages或其他内容),具有以下内容:

import sys
sys.setdefaultencoding('utf-8')

也许您可能还需要在文件中指定编码:

# -*- coding: UTF-8 -*-
import sys,time

编辑:更多信息可以在优秀的《 Dive into Python》一书中找到


2
setdefaultencoding()在sys中不再存在(根据模块文档,从v2.0开始)。
乔恩·凯奇

我目前无法证明这一点,但是我知道我在更高版本的Windows-2.5上使用了该技巧。
BartoszRadaczyński,2009年

6
好的,一段时间后,我发现:“此功能仅旨在由站点模块实现以及需要的地方由sitecustomize使用。一旦由站点模块使用,便将从sys模块的名称空间中删除。 ”
BartoszRadaczyński,2009年

4
实际上,您可以将Windows控制台设置为utf-8。您需要说chcp 65001,它将是unicode。
BartoszRadaczyński10年

4
明确说明:更改默认编码是一个非常糟糕的主意。这类似于使您的断腿瘫痪并走路,好像什么也没发生,而不是让医生正确地固定骨头。所有处理Unicode文本的代码都应该一致地这样做,而不是依赖于隐式编码/解码。
马丁·彼得斯

1

肯尼迪·塞巴斯蒂安(JF Sebastian)的答案与之相关,但更为直接。

如果在打印到控制台/终端时遇到此问题,请执行以下操作:

>set PYTHONIOENCODING=UTF-8

3
set PYTHONIOENCODING=UTF-8可能导致变为乱码如果控制台使用不同的编码,例如CP437。cp65001有很多问题。要将Unicode打印到Windows控制台,应按照我的答案中的WriteConsoleW()建议使用()Unicode API,其中Unicode API 仅用于替换当前OEM代码页中无法表示的字符(即使此类字符也可以使用)。如果将输出重定向到文件,则可以使用。PYTHONIOENCODING?WriteConsoleW()PYTHONIOENCODING
jfs

1

Python 3.6 Windows7:有几种启动python的方法,您可以使用python控制台(上面带有python徽标)或Windows控制台(上面写有cmd.exe)。

我无法在Windows控制台中打印utf8字符。打印utf-8字符会引发此错误:

OSError: [winError 87] The paraneter is incorrect 
Exception ignored in: (_io-TextIOwrapper name='(stdout)' mode='w' ' encoding='utf8') 
OSError: [WinError 87] The parameter is incorrect 

在尝试并且无法理解以上答案之后,我发现这只是一个设置问题。右键单击cmd控制台窗口的顶部,在选项卡上font选择lucida控制台。


0

詹姆斯·苏拉克(James Sulak)问,

有什么办法可以使Python自动打印?而不是在这种情况下失败?

其他解决方案建议我们尝试修改Windows环境或替换Python的Windows环境。 print()功能。下面的答案更接近满足Sulak的要求。

在Windows 7下,可以使Python 3.5打印Unicode而不会抛出 UnicodeEncodeError如下内容:

    代替:    print(text)
    替代:     print(str(text).encode('utf-8'))

现在,Python不会抛出异常,而是将不可打印的Unicode字符显示为\ xNN十六进制代码,例如:

  Halmalo n \ xe2 \ x80 \ x99 \ xc3 \ xa9tait加上qu \ xe2 \ x80 \ x99un点黑色

代替

  Halmalon'était加qu'un点黑色

当然,后者是更可取的ceteris paribus,但否则前者对于诊断消息是完全准确的。因为它将Unicode显示为文字字节值,所以前者还可以帮助诊断编码/解码问题。

注意:str()上面的调用是必需的,因为否则encode()会导致Python拒绝Unicode字符作为数字元组。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.