首页 > 解决方案 > 错误=surrogateescape vs 错误=替换

问题描述

我正在尝试打开这样的文件:

with open("myfile.txt", encoding="utf-8") as f:

myfile.txt来自我的应用程序的用户。90% 的情况下,该文件以非 UTF-8 格式出现,这会导致应用程序退出,因为它无法正确读取它。错误就像'utf-8' codec can't decode byte 0x9c

我在谷歌上搜索了一下,发现了一些 Stackoverflow 的答案,说要像这样打开我的文件:

with open("myfile.txt", encoding="utf-8", errors="surrogateescape") as f:

但其他答案说要使用:

with open("myfile.txt", encoding="utf-8", errors="replace") as f:

那么修复文件中非 UTF-8 字节的errors="replace"方法有什么区别呢?errors="surrogateescape"

标签: pythonfileencodingutf-8

解决方案


医生说:

'replace':用合适的替换标记替换;Python 将使用官方的 U+FFFD REPLACEMENT CHARACTER 来解码内置编解码器,并使用“?” 关于编码。在 replace_errors() 中实现。
...
'surrogateescape':解码时,将字节替换为从 U+DC80 到 U+DCFF 的各个代理代码。然后,当编码数据时使用“surrogateescape”错误处理程序时,此代码将转回相同的字节。(有关更多信息,请参阅 PEP 383。)

这意味着replace,任何有问题的字节都将被替换为相同的U+FFFD替换字符,而surrogateescape每个字节都将被替换为不同的值。例如 a'\xe9'将替换为a'\udce9'和。'\xe8''\udce8'

因此,使用替换,您可以获得有效的 unicode 字符,但会丢失文件的原始内容,而使用 surrogateescape,您可以知道原始字节(甚至可以使用 完全重建它.encode(errors='surrogateescape')),但是您的 unicode 字符串不正确,因为它包含原始代理代码。

长话短说:如果原始的违规字节无关紧要,并且您只想摆脱错误,replace这是一个不错的选择,如果您需要保留它们以供以后处理,surrogateescape则是要走的路。


surrogateescape当您的文件主要包含 ascii 字符和一些(重音)非 ascii 字符时,它有一个非常好的功能。而且您也有用户偶尔使用非 UTF8 编辑器修改文件(或未能声明 UTF8 编码)。在这种情况下,您会以一个文件结尾,该文件主要包含 utf8 数据和一些不同编码的字节,对于非英语西欧语言(如法语、葡萄牙语和西班牙语)的 Windows 用户,通常为 CP1252。在这种情况下,可以构建一个转换表,将代理字符映射到 cp1252 字符集中的等效字符:

# first map all surrogates in the range 0xdc80-0xdcff to codes 0x80-0xff
tab0 = str.maketrans(''.join(range(0xdc80, 0xdd00)),
             ''.join(range(0x80, 0x100)))
# then decode all bytes in the range 0x80-0xff as cp1252, and map the undecoded ones
#  to latin1 (using previous transtable)
t = bytes(range(0x80, 0x100)).decode('cp1252', errors='surrogateescape').translate(tab0)
# finally use above string to build a transtable mapping surrogates in the range 0xdc80-0xdcff
#  to their cp1252 equivalent, or latin1 if byte has no value in cp1252 charset
tab = str.maketrans(''.join(chr(i) for i in range(0xdc80, 0xdd00)), t)

然后,您可以解码包含 utf8 和 cp1252 的 mojibake 的文件:

with open("myfile.txt", encoding="utf-8", errors="surrogateescape") as f:
    for line in f:                     # ok utf8 has been decoded here
        line = line.translate(tab)     # and cp1252 bytes are recovered here

我已经成功地多次使用该方法来恢复生成为 utf8 并在 Windows 机器上使用 Excel 编辑过的 csv 文件。

相同的方法可用于从 ascii 派生的其他字符集


推荐阅读