我正在编写一个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'))
现在它将有问题的字符“—”替换为
?
。 这不是理想的情况(连字符应该是更好的替代品),但足以满足我的目的。
我的解决方案中有几处是我不喜欢的。
问题不在于破折号(我可以想出几种方法来解决这个特定问题),但我需要编写健壮的代码。 我正在向页面提供来自数据库的数据,并且该数据可以返回。 我可以预见许多其他冲突的情况:“Á”U+00c1(在我的数据库中可能)可以转换为 CP-850(西欧语言的 DOS/Windows 控制台编码),但不能转换为 CP-437(美国编码)英语,许多 Windows 安装中默认为英语)。
是否有更好的解决方案使我的代码与输出接口编码无关?
我看到了三种解决方案:
更改输出编码,因此它将始终输出UTF-8。参见例如在 Python 中管道 stdout 时设置正确的编码,但我无法让这些示例工作。
以下示例代码使输出了解您的目标字符集。
# -*- 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
,使用该机制对输出进行正确编码,您可以在必要时简单地将 print 替换为 myprint
,而不会让整个代码看起来很难看。在软件开始时全局重置输出编码:
页面 http://www.macfreek.nl/memory/Encoding_of_Python_stdout 很好地总结了如何更改输出编码。尤其是“StreamWriter Wrapper around Stdout”部分很有趣。本质上它是说要像这样改变 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 中使用,您可以将 '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
根据 Dirk Stöcker 的回答,这里有一个 Python 3 打印函数的简洁包装函数。就像使用打印一样使用它。
作为一个额外的好处,与其他答案相比,由于最后的解码步骤,这不会将您的文本打印为字节数组('b“内容”'),而是打印为普通字符串('内容')。
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')
我对此进行了更深入的研究,发现最好的解决方案就在这里。
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'))
出于调试目的,您可以使用
print(repr(data))
。
要显示文本,请始终打印 Unicode。不要在脚本中硬编码环境的字符编码,例如 Cp850。要解码 HTTP 响应,请参阅在 Python 中获取 HTTP 响应的字符集/编码的好方法。
要将 Unicode 打印到 Windows 控制台,您可以 使用
win-unicode-console
包。
如果您使用 Windows 命令行打印数据,则应该使用
chcp 65001
这对我有用!
如果您使用Python 3.6(可能是3.5或更高版本),它不会再给我这个错误。 我遇到了类似的问题,因为我使用的是 v3.4,但在卸载并重新安装后它就消失了。