文档说:
“替换”:
用合适的替换标记替换; Python 将使用官方的 U+FFFD REPLACEMENT CHARACTER 作为内置编解码器进行解码,使用“?”进行编码。在 replace_errors() 中实现。
...
'surrogateescape':解码时,将字节替换为从 U+DC80 到 U+DCFF 的各个代理代码。然后,当编码数据时使用“surrogateescape”错误处理程序时,此代码将转回相同的字节。 (有关更多信息,请参阅 PEP 383。)
这意味着对于replace,任何有问题的字节都将替换为相同的U+FFFD 替换字符,而对于surrogateescape,每个字节都将替换为不同的值。例如,'\xe9' 将替换为 '\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 派生的其他字符集