UnicodeEncodeError: ‘ charmap’codec 不能将字符映射编码为 < unDefinition > ,print 函数

我正在编写一个 Python (Python 3.3)程序,使用 POST 方法向网页发送一些数据。主要是为了调试过程,我得到的页面结果,并显示在屏幕上使用 print()函数。

代码是这样的:

conn.request("POST", resource, params, headers)
response = conn.getresponse()
print(response.status, response.reason)
data = response.read()
print(data.decode('utf-8'));

HTTPResponse .read()方法返回一个编码页面的 bytes元素(这是一个格式良好的 UTF-8文档)直到我停止使用 IDLE GUI for Windows 而改用 Windows 控制台之前,这看起来还不错。返回的页面有一个 U + 2014字符(em-ash) ,打印函数在 Windows GUI 中翻译得很好(我推测是代码页1252) ,但在 Windows 控制台(代码页850)中没有。考虑到 strict的默认行为,我得到以下错误:

UnicodeEncodeError: 'charmap' codec can't encode character '\u2014' in position 10248: character maps to <undefined>

我可以用这个相当丑陋的代码修复它:

print(data.decode('utf-8').encode('cp850','replace').decode('cp850'))

现在它将冒犯字符“ー”替换为 ?。不是理想的情况下(连字符应该是一个更好的替代品) ,但足以满足我的目的。

从我的解决方案中,有几件事我不喜欢。

  1. 所有的解码、编码和解码过程中,代码都很丑陋。
  2. 只有这个案子才能解决问题。如果我为使用其他编码的系统移植程序(拉丁文 -1、 cp437、回到 cp1252等等) ,它应该能够识别目标编码。没有。(例如,当再次使用 IDLE GUI 时,emash 也会丢失,这在以前从未发生过)
  3. 如果把破折号翻译成连字符而不是审讯键就更好了。

问题不在于 emash (我可以想出几种解决这个特殊问题的方法) ,而是我需要编写健壮的代码。我提供的数据从数据库和数据可以回来的页面。我可以预见到许多其他相互矛盾的情况: ‘ Á’U + 00 c1(在我的数据库中是可能的)可以转换成 CP-850(DOS/Windows 控制台西欧语言编码) ,但不能转换成 CP-437(美国英语编码,这在许多 Windows 安装中是默认的)。

那么,问题来了:

有没有更好的解决方案,使我的代码不可知的输出接口编码?

497774 次浏览

我看到了三种解决方案:

  1. 更改输出编码,使其始终输出 UTF-8。参见例如 在 Python 中管道化 stdout 时设置正确的编码,但是我无法让这些示例工作。

  2. 下面的示例代码使输出能够识别目标字符集。

    # -*- coding: utf-8 -*-
    import sys
    
    
    print sys.stdout.encoding
    print u"Stöcker".encode(sys.stdout.encoding, errors='replace')
    print u"Стоескер".encode(sys.stdout.encoding, errors='replace')
    

    此示例将我名字中任何不可打印的字符正确地替换为问号。

    如果你创建一个定制的打印函数,例如 myprint,使用这种机制来正确地编码输出,你可以在任何需要的地方用 myprint代替 print,而不会使整个代码看起来很丑陋。

  3. 在软件开始时全局重置输出编码:

    http://www.macfreek.nl/memory/Encoding_of_Python_stdout页有一个很好的摘要,说明了如何更改输出编码。特别是“围绕 Stdout 的 StreamWriter Wrapper”部分非常有趣。本质上,它是这样改变 I/O 编码函数的:

    在 Python 2中:

    if sys.stdout.encoding != 'cp850':
    sys.stdout = codecs.getwriter('cp850')(sys.stdout, 'strict')
    if sys.stderr.encoding != 'cp850':
    sys.stderr = codecs.getwriter('cp850')(sys.stderr, 'strict')
    

    在 Python 3中:

    if sys.stdout.encoding != 'cp850':
    sys.stdout = codecs.getwriter('cp850')(sys.stdout.buffer, 'strict')
    if sys.stderr.encoding != 'cp850':
    sys.stderr = codecs.getwriter('cp850')(sys.stderr.buffer, 'strict')
    

    如果在输出 HTML 的 CGI 中使用,您可以用“ xmlcharrefreplace”替换“ strong”,以获得非打印字符的 HTML 编码标记。

    请随意修改方法,设置不同的编码,... ..。注意,它仍然不能输出非指定的数据。因此,任何数据、输入和文本都必须能够正确地转换成 Unicode:

    # -*- coding: utf-8 -*-
    import sys
    import codecs
    sys.stdout = codecs.getwriter("iso-8859-1")(sys.stdout, 'xmlcharrefreplace')
    print u"Stöcker"                # works
    print "Stöcker".decode("utf-8") # works
    print "Stöcker"                 # fails
    

根据 Dirk Stöcker 的回答,这里有一个 Python 3 print 函数的简洁包装函式。就像使用印刷品一样使用它。

作为额外的好处,与其他答案相比,这不会打印你的文本作为一个字节数组(“ b”“ content”) ,但作为正常的字符串(“ content”) ,因为最后一个解码步骤。

def uprint(*objects, sep=' ', end='\n', file=sys.stdout):
enc = file.encoding
if enc == 'UTF-8':
print(*objects, sep=sep, end=end, file=file)
else:
f = lambda obj: str(obj).encode(enc, errors='backslashreplace').decode(enc)
print(*map(f, objects), sep=sep, end=end, file=file)


uprint('foo')
uprint(u'Antonín Dvořák')
uprint('foo', 'bar', u'Antonín Dvořák')

出于调试目的,可以使用 print(repr(data))

若要显示文本,请始终打印 Unicode。不要在脚本中硬编码环境的字符编码,比如 Cp850。要解码 HTTP 响应,请参见 在 Python 中获取 HTTP 响应的字符集/编码的一种好方法

要将 Unicode 打印到 Windows 控制台,可以使用 使用 win-unicode-console软件包

如果您使用 Python 3.6(可能是3.5或更高版本) ,它不会再给我这个错误。我也遇到过类似的问题,因为我使用的是 v3.4,但是在卸载和重新安装之后它就消失了。

我深入研究了一下,发现最好的解决方案就在这里。

Http://blog.notdot.net/2010/07/getting-unicode-right-in-python

在我的例子中,我解决了“ UnicodeEncodeError: ‘ charmap’codec 不能编码字符”的问题

原始码:

print("Process lines, file_name command_line %s\n"% command_line))

新代码:

print("Process lines, file_name command_line %s\n"% command_line.encode('utf-8'))

如果使用 Windows 命令行打印数据,则应使用

chcp 65001

这招对我管用!