Python加载'utf-16'文件无法解码'\ u0153'

Rop*_*tah 5 python character-encoding

我有一个编码的文本文件,utf-16它会引发以下字符的异常:'\u0153'.

UnicodeEncodeError:'charmap'编解码器无法对字符'\ u0153'进行编码

我正在使用一个非常简单的脚本来加载文件,我也尝试忽略错误而无济于事.我究竟做错了什么?

with open(filename, "r", encoding="utf-16", errors='replace') as data_file:    
    print(data_file.read())
Run Code Online (Sandbox Code Playgroud)

这是文件的一部分,它打破了:

["Xinhua","Ürümqi"]

编辑: 不知道为什么我的问题被误解了.希望这是更好的形成.

我该如何用Python阅读这个文件?

示例文件链接(UTF-16-LE文件)包含:

["Xinhua","Ürümqi"]

为什么这段代码不起作用?

with open(filename, "r", encoding="utf-16", errors='replace') as data_file:    
    print(data_file.read())
Run Code Online (Sandbox Code Playgroud)

zwo*_*wol 7

最初困扰你的例外是因为你在终端模拟器中运行Python(或者可能是"控制台窗口"是一个更熟悉的术语?),它无法显示Unicode中的所有字符.要解决这个问题,你需要让自己拥有一个支持Unicode的终端模拟器,然后确保Python 知道它在支持Unicode的终端模拟器中运行.如果您不知道如何操作,请在superuser.com上提出一个新问题,指定您的操作系统.

我的终端模拟器可以显示Unicode中的所有字符,假设所有必需的字体都可用,并且Python知道这一点,所以我可以这样做而不会得到异常:

>>> with open("countryCity2.json", "r", encoding="utf-16") as f:
...   x = f.read()
... 
>>> print(x)
["Xinhua","Ürümqi"]
Run Code Online (Sandbox Code Playgroud)

但是,这不是你唯一的问题.您的输入文件已编码其编码.Ürümqi不是任何语言中有意义的字符序列.但是,它符合已经从遗留编码转换为UTF-8的文本的特征mojibake模式,然后 - 错误地 - 再次转换为Unicode编码.我们可以通过将它1:1转换为字节并查看是否获得有效的UTF-8字节序列来测试它:

>>> print(x.encode("iso-8859-1").decode("utf-8"))
["Xinhua","Ürümqi"]
Run Code Online (Sandbox Code Playgroud)

" Ürümqi "是一个真实的词,可能与" 新华 " 一起出现.此外,如果文本没有被错误转换为UTF-8,我们会看到一个例外:

>>> "Ürümqi".encode("iso-8859-1").decode("utf-8")
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
UnicodeDecodeError: 'utf-8' codec can't decode byte 0xdc in position 0:
  invalid continuation byte
Run Code Online (Sandbox Code Playgroud)

所以这个假设得到了证实.

在一个必须处理大量文件的程序中,这些文件的编码可能会或可能不会以这种方式被破坏,我会做这样的事情:

for fname in input_files:
    with open(fname, "r", encoding="utf-16") as f:
        contents = f.read()
    try:
        contents = contents.encode("iso-8859-1").decode("utf-8")
    except (UnicodeEncodeError, UnicodeDecodeError):
        pass
    process_file(fname, contents)
Run Code Online (Sandbox Code Playgroud)

我在这里使用ISO 8859.1编码并不是因为文本实际上或者实际上是在那个编码中,而是因为Python的iso-8859-1编解码器是从字符U + 0000..U + 00FF到字节0x00..0xFF的身份映射.(从技术上讲,这意味着它实现了IANA ISO_8859-1:1987而不是原始的ECMA-94:1985代码页,这使得0x00..0x1F和0x7F..0x9F范围未定义.)即,

>>> "".join(chr(c) for c in range(256)).encode('iso-8859-1') == bytes(range(256))
True
Run Code Online (Sandbox Code Playgroud)

因此,只要您将二进制数据错误地转换为Unicode,就可以恢复原始数据.encode('iso-8859-1').

注意:上面的所有代码片段都是Python 3.