python - 错误=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"
解决方案
医生说:
'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 派生的其他字符集
推荐阅读
- sql-server - 仅在较新的情况下从暂存到最终插入
- tensorflow - 为什么 TFLite Metal 中线程组 num (work_group_launch_order) 的顺序很重要?
- tkinter - 在 tkinter 中单击按钮时更改按钮文本颜色
- android - 如何获取设备 ID 以使用 Android Management API?
- python - 如果行的第一个元素为0而不在python中使用numpy,如何删除二维矩阵行
- r - 用于 grep 模式并将其打印为下一行中的列的代码,直到模式重新出现
- visual-studio-code - 如何在代码VS Studio Code之前关闭波浪形红线
- google-api - 如何获取访问令牌以与谷歌存储 URL 一起使用
- java - 有没有办法合并这2个?
- flutter - 如何启用 SystemChrome - Flutter 中的 SystemUiOverlayStyle