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

Car*_*zón 149 python encoding encode decode

我正在编写一个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'));
Run Code Online (Sandbox Code Playgroud)

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>
Run Code Online (Sandbox Code Playgroud)

我可以使用这个非常难看的代码修复它:

print(data.decode('utf-8').encode('cp850','replace').decode('cp850'))
Run Code Online (Sandbox Code Playgroud)

现在用一个替换有问题的字符" - " ?.不是理想的情况(连字符应该是一个更好的替代品),但足够我的目的.

我的解决方案中有几件我不喜欢的东西.

  1. 所有解码,编码和解码都是丑陋的代码.
  2. 它解决了这个案例的问题.如果我使用其他编码(latin-1,cp437,返回cp1252等)为系统移植程序,它应该识别目标编码.它不是.(例如,当再次使用IDLE GUI时,emdash也会丢失,之前没有发生过)
  3. 如果将emdash翻译成连字符而不是审讯爆炸会更好.

问题不在于emdash(我可以想到解决这个问题的几种方法),但我需要编写健壮的代码.我正在向页面提供来自数据库的数据,并且数据可以返回.我可以预见到许多其他相互矛盾的情况:'Á'U+ 00c1(在我的数据库中可能)可以转换为CP-850(西欧语言的DOS/Windows控制台编码)但不能转换为CP-437(美国的编码)英语,在许多Windows instalations中是默认的).

那么,问题是:

有没有更好的解决方案使我的代码与输出接口编码无关?

Dir*_*ker 100

我看到三个解决方案:

  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')
    
    Run Code Online (Sandbox Code Playgroud)

    此示例使用问号正确替换了我名称中的任何不可打印字符.

    如果您创建一个自定义打印功能,例如调用myprint,使用该机制正确编码输出,您可以简单地用myprint必要的替换打印,而不会使整个代码看起来很丑.

  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')
    
    Run Code Online (Sandbox Code Playgroud)

    在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')
    
    Run Code Online (Sandbox Code Playgroud)

    如果在CGI输出HTML中使用,您可以将'strict'替换为'xmlcharrefreplace',以获取不可打印字符的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
    
    Run Code Online (Sandbox Code Playgroud)

  • Python 3:`sys.stdout = io.TextIOWrapper(sys.stdout.detach(),sys.stdout.encoding,'replace')`.或者使用''backslashreplace'来保留序数值(但可能不对齐输出格式). (4认同)
  • @eryksun:或使用`win-unicode-console`包直接打印Unicode,无论`chcp`是什么.或者设置`PYTHONIOENCODING =:replace` envvar(在脚本之外).如果已经打印Unicode,则无需更改脚本,而是配置您的环境. (2认同)

Jel*_*sen 29

根据DirkStöcker的回答,这里是Python 3的打印功能的整洁包装函数.像使用print一样使用它.

作为额外的奖励,与其他答案相比,这不会将您的文本打印为bytearray('b"内容"'),而是作为普通字符串('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')
Run Code Online (Sandbox Code Playgroud)


jfs*_*jfs 25

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

要显示文本,请始终打印Unicode.不要硬编码环境中的字符编码,例如cp850脚本内部.要解码http响应,请参阅在Python中获取HTTP响应的字符集/编码的好方法.

要将Unicode打印到Windows控制台,您可以使用win-unicode-console.


lee*_*onq 19

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

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

在我的情况下,我解决了"UnicodeEncodeError:'charmap'编解码器无法编码字符"

原始代码:

print("Process lines, file_name command_line %s\n"% command_line))
Run Code Online (Sandbox Code Playgroud)

新代码:

print("Process lines, file_name command_line %s\n"% command_line.encode('utf-8'))  
Run Code Online (Sandbox Code Playgroud)

  • 在我的情况下,编码('utf-8')是件好事.谢谢 (2认同)

Žel*_*jić 15

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

chcp 65001
Run Code Online (Sandbox Code Playgroud)

这对我有用!

  • 不好 在Windows 8之前,输出的错误特别严重,并且非ASCII输入在Windows 10之前无法正常工作。升级到Python 3.6或安装`win_unicode_console`是在Windows控制台中获得正确的Unicode支持的方法。 (2认同)