UnicodeEncodeError:“charmap”编解码器无法编码 – 字符映射到<undefined>,打印功能

我正在编写一个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文档)直到我停止使用Windows的IDLE GUI并改用Windows控制台,这似乎没有问题。 返回的页面有一个U + 2014字符(em-dash),它在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时,emdash也会丢失,这在以前没有发生过)
  3. 如果这个模版翻译成连字符而不是盘问,会更好。

问题不在于emdash(我可以想到几种方法来解决特别的问题),但是我需要编写健壮的代码。 我用来自数据库的数据提供页面,数据可以回来。 我可以预见许多其他冲突的情况:一个'U + 00c1(可能在我的数据库中)可以转换成CP-850(西欧语言的DOS / Windows控制台编码),但不转换成CP-437(编码为美国英语,这在许多Windows安装中是默认的)。

所以,这个问题:

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

我看到了三个解决方案:

  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代替打印,而不会使整个代码看起来很丑。

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

    页面http://www.macfreek.nl/memory/Encoding_of_Python_stdout有一个很好的总结如何做改变输出编码&#x3002; 特别是“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') 

    如果在CGI输出HTML中使用,您可以用'xmlcharrefreplace'来替换'strict',以获得不可打印字符的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 

基于DirkStöcker的回答,下面是Python 3打印函数的一个简洁的包装函数。 像使用打印一样使用它。

作为额外的好处,与其他答案相比,由于最后一个解码步骤,这不会将您的文本打印为bytearray('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软件包 。

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

 chcp 65001 

这对我有用!

我深入挖掘,发现最好的解决方案在这里。

http://blog.notdot.net/2010/07/Getting-unicode-right-in-Python

在我的情况下,我解决了“UnicodeEncodeError:”charmap“编解码器不能编码字符”

原始码:

 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')) 

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