Windows上Python的Utf-8

6

我有一个需要读取、解析等操作的html文件,它是unicode编码(我在记事本中看到了),但当我尝试

infile = open("path", "r") 
infile.read()

它失败了,我遇到了著名的错误:

UnicodeEncodeError: 'charmap'编解码器无法对位置xx的字符进行编码:该字符映射到未定义

所以为了测试,我尝试将文件内容复制粘贴到一个新文件中,并以utf-8格式保存,然后像这样使用codecs打开它:

inFile = codecs.open("path", "r", encoding="utf-8")
outputStream = inFile.read()

但是我收到了这个错误信息:

UnicodeEncodeError:'charmap'编解码器无法将字符u'\ufeff'编码在位置0:字符映射到未定义

我真的不明白,因为我使用utf8创建了这个文件。


1
那是一个 unicode BOM,看起来是 utf-16,你可以尝试传递 encoding='utf-16' - EdChum
@EdChum,我已经尝试了,但是响应是:
UnicodeError: UTF-16 流不以 BOM 开头。
- taspai
你能否发布原始输入数据的前几行或文件链接,谢谢。另一个选项是跳过前几个字符,但实际上应该能够无问题地打开它。 - EdChum
@AlastairMcCormack 是的,你说得对,我在向终端打印时遇到了错误!所以一切正常,但终端无法打印一些符号? - taspai
可以的。你能否更新你的问题,以便它反映出问题所在?UnicodeEncodeErrors 应该包含故障所在的行号,这样你就可以识别出失败的语句。 - Alastair McCormack
显示剩余2条评论
3个回答

6
UnicodeEncodeError 表示在Unicode文本转换为字节(即实际代码试图输出到Windows控制台时)时出现了错误。参见 Python, Unicode, and the Windows console
上面的链接可以解决UnicodeEncodeError。接下来的问题是找出文本文件中所使用的字符编码。如果 notepad.exe 正确显示文本,则表示它使用的是 locale.getprefferedencoding(False)(例如Windows上的cp1252),或者该文件具有BOM(字节顺序标记)。
如果您确定编码是utf-8,则直接将其传递给open()。不要使用codecs.open()
with open('path', encoding='utf-8') as file:
    html = file.read()

有时,输入的文本可能会使用多个(不一致的)编码方式进行编码,例如,智能引号可能使用cp1252进行编码,而其余的HTML则是utf-8 -- 您可以使用bs4.UnicodeDammit来修复它。另请参阅在Python中获得HTTP响应的字符集/编码的好方法

如果Notepad显示“Unicode”(正如OP所说),那么它表示UTF-16。其他编码通常称为“ANSI”(cp1252和同类)和“UTF-8”(这是带有BOM的UTF-8)。 - roeland
1
@roeland:是的。从问题中可以这样解释:“它在unicode上编码(我用记事本看到了)”。但这个理论的问题在于,codecs.open("path", encoding='utf-8').read()返回u'\ufeff',即更可能是utf-8-sig'utf-8'编码对于BOM_UTF16_BEBOM_UTF16_LE都会失败。 - jfs
是的,这个问题有点令人困惑,因为它涉及到两个文件,一个原始的“Unicode”文件和他重新保存为“UTF-8”的文件。 - roeland
@roeland:无论如何,问题是UnicodeEncodeError,即当OP尝试将Unicode文本打印到Windows控制台时。 - jfs
啊哈,我明白了。那真是微妙。 - roeland

1
原始文件可能使用的编码是 utf-16(Windows 使用术语 UNICODE 表示该编码)。
在 Windows 上,UTF-8 编码的文件通常以魔数 b"\xef\xbb\xbf"(U+FEFF 的 UTF-8 编码)开头,以便读取该文件的应用程序知道它被保存为 UTF-8 而不是某个 ANSI 代码页。utf8-sig 可自动丢弃该字符。

2
顺便提一下:不要使用 codecs.open。在 Py3 上,您可以将 encoding 参数传递给常规的 open,而在 Py2.7 上,您可以导入 io.open(与 Py3 的内置 open 相同)并执行相同操作。codecs.open 有一些愚蠢的怪癖(例如不执行通用换行符处理)。 - ShadowRanger

1
在期待OP更新问题以反映实际问题时,问题是终端编码未定义引起的。
Windows控制台在Unicode支持方面声名狼藉。为了得到最佳支持,请参阅https://pypi.python.org/pypi/win_unicode_console。基本上,安装“win_unicode_console”(pip install win_unicode_console)。然后在你的代码顶部:
import win_unicode_console
win_unicode_console.enable()

您可能还需要使用适当的字体-请参见https://dev59.com/-2025IYBdhLWcg3w3J5H#5750227

由于您正在使用带有UTF-8 BOM的输入,因此应使用utf_8_sig编解码器以便在处理内容之前删除BOM。

由于这是Python 3,所以在使用open()时不需要使用codecs模块来设置编码。

把它们放在一起,它会看起来像:

import win_unicode_console
win_unicode_console.enable()

infile = open("path", "r", encoding="utf_8_sig")

最好避免修改脚本。你可以使用run模块运行它(这是win-unicode-console的一部分):py -m run your-unicode-printing-script.py 或者如果适用于你的情况,把win_unicode_console.enable()调用放到sitecustomizeusercustomize模块中。 - jfs

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接