我正在尝试打开这样的文件:
with open("myfile.txt", encoding="utf-8") as f:
但是myfile.txt
来自我应用程序的用户。而且有90%的时间,此文件以非UTF-8格式出现,导致应用程序退出,因为它无法正确读取文件。错误就像'utf-8' codec can't decode byte 0x9c
我已经在Google上进行了搜索,发现了一些Stackoverflow答案,说可以这样打开我的文件:
with open("myfile.txt", encoding="utf-8", errors="surrogateescape") as f:
但其他答案说要使用:
with open("myfile.txt", encoding="utf-8", errors="replace") as f:
那么errors="replace"
和errors="surrogateescape"
之间有什么区别,哪一个可以修复文件中的非UTF-8字节?
答案 0 :(得分:2)
医生说:
'替换': 用合适的替换标记物进行替换; Python将使用官方的U + FFFD REPLACEMENT CHARACTER用于解码时的内置编解码器,以及编码时的“?”。在replace_errors()中实现。
...
'surrogateescape':在解码时,将字节替换为范围从U + DC80到U + DCFF的各个替代代码。当在编码数据时使用“ surrogateescape”错误处理程序时,此代码将被转换回相同的字节。 (有关详情,请参见PEP 383。)
这意味着使用replace
,任何有问题的字节将被相同的U+FFFD
替换字符替换,而使用surrogateescape
,则将每个字节替换为不同的值。例如,将'\xe9'
替换为'\udce9'
,将'\xe8'
替换为'\udce8'
。
因此,使用replace时,您将获得有效的unicode字符,但会丢失文件的原始内容,而使用surrogateescape时,您可以知道原始字节(甚至可以使用.encode(errors='surrogateescape')
进行完全重建),但是您的unicode字符串不正确,因为它包含原始代理代码。
长话短说:如果原始违规字节无所谓,而您只是想摆脱错误,那么replace
是一个不错的选择,如果您需要保留它们以便以后处理,{{1 }}是必经之路。
surrogateescape
具有非常好的功能,当您的文件主要包含ascii字符和一些(带重音)非ascii字符时。而且您还有一些用户有时会使用非UTF8编辑器来修改文件(或者无法声明UTF8编码)。在这种情况下,您将获得一个文件,该文件主要包含utf8数据和一些采用不同编码的字节,对于非英语西欧语言(例如法语,西班牙语的葡萄牙语)的Windows用户,通常为CP1252。在这种情况下,可以构建一个转换表,该转换表会将代理字符映射到cp1252字符集中的等效字符:
surrogateescape
然后您可以解码包含utf8和cp1252的mojibake的文件:
# 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格式生成并已在Windows计算机上用Excel编辑过的csv文件。
相同的方法可以用于其他从ascii导出的字符集
答案 1 :(得分:0)
我的问题是文件中的行具有混合的编码类型。
解决方法是删除encoding="utf-8"
并添加errors="replace"
。因此open()
行的结尾将是这样:
with open("myfile.txt", errors="replace") as f:
如果可以检测文件的编码类型,我将其添加为encoding
参数,但是很遗憾,无法检测到它。