读取文件时Python中的UnicodeDecodeError,如何忽略错误并跳转到下一行?
Posted
技术标签:
【中文标题】读取文件时Python中的UnicodeDecodeError,如何忽略错误并跳转到下一行?【英文标题】:UnicodeDecodeError in Python when reading a file, how to ignore the error and jump to the next line? 【发布时间】:2014-08-28 07:04:26 【问题描述】:我必须将文本文件读入 Python。文件编码为:
file -bi test.csv
text/plain; charset=us-ascii
这是一个第三方文件,我每天都会得到一个新的,所以我宁愿不改变它。该文件包含非 ascii 字符,例如 Ö。我需要使用 python 读取这些行,并且我可以忽略具有非 ascii 字符的行。
我的问题是,当我在 Python 中读取文件时,在到达存在非 ascii 字符的行时出现 UnicodeDecodeError,并且我无法读取文件的其余部分。
有没有办法避免这种情况。如果我试试这个:
fileHandle = codecs.open("test.csv", encoding='utf-8');
try:
for line in companiesFile:
print(line, end="");
except UnicodeDecodeError:
pass;
然后当出现错误时,for 循环结束,我无法读取文件的其余部分。我想跳过导致错误的行并继续。如果可能,我宁愿不对输入文件进行任何更改。
有没有办法做到这一点? 非常感谢。
【问题讨论】:
为什么在 Python 3 中使用codecs.open()
? open()
处理 UTF-8 很好。
我也试过用open,我得到同样的错误
你知道文件真正使用的是什么编码吗?这显然不是us-ascii
输出中显示的us-ascii
,因为它包含非ascii 字符。
@Chicoscience:我没有解决你的问题;我很困惑你为什么在这里使用codecs.open()
,因为它不如open()
。
没问题,Martijn,谢谢! Dano,这对我来说也很奇怪,编码是 ascii,但显然不是 ascii
【参考方案1】:
您的文件似乎没有使用 UTF-8 编码。打开文件时使用正确的编解码器很重要。
您可以告诉open()
如何处理解码错误,使用errors
关键字:
errors 是一个可选字符串,它指定如何处理编码和解码错误——这不能在二进制模式下使用。有多种标准错误处理程序可用,但任何已向
如果出现编码错误,codecs.register_error()
注册的错误处理程序名称也是有效的。标准名称是:'strict'
会引发ValueError
异常。默认值None
具有相同的效果。'ignore'
忽略错误。请注意,忽略编码错误可能会导致数据丢失。'replace'
会在存在格式错误的数据的位置插入替换标记(例如“?”)。'surrogateescape'
将在从 U+DC80 到 U+DCFF 的 Unicode Private Use Area 中将任何不正确的字节表示为代码点。当写入数据时使用surrogateescape
错误处理程序时,这些私有代码点将被转换回相同的字节。这对于处理未知编码的文件很有用。'xmlcharrefreplace'
仅在写入文件时受支持。编码不支持的字符将替换为相应的 XML 字符引用&#nnn;
。'backslashreplace'
(也仅在编写时支持)用 Python 的反斜杠转义序列替换不受支持的字符。
使用除'strict'
('ignore'
、'replace'
等)以外的任何内容打开文件后,您就可以读取文件而不会引发异常。
请注意,解码发生在每个缓冲的数据块上,而不是每个文本行。如果您必须逐行检测错误,请使用surrogateescape
处理程序并测试读取的每一行代码点是否在代理范围内:
import re
_surrogates = re.compile(r"[\uDC80-\uDCFF]")
def detect_decoding_errors_line(l, _s=_surrogates.finditer):
"""Return decoding errors in a line of text
Works with text lines decoded with the surrogateescape
error handler.
Returns a list of (pos, byte) tuples
"""
# DC80 - DCFF encode bad bytes 80-FF
return [(m.start(), bytes([ord(m.group()) - 0xDC00]))
for m in _s(l)]
例如
with open("test.csv", encoding="utf8", errors="surrogateescape") as f:
for i, line in enumerate(f, 1):
errors = detect_decoding_errors_line(line)
if errors:
print(f"Found errors on line i:")
for (col, b) in errors:
print(f" col + 1:2d: b[0]:02x")
请注意,并非所有解码错误都可以正常恢复。虽然 UTF-8 被设计为在面对小错误时保持稳健,但其他多字节编码(如 UTF-16 和 UTF-32)无法处理丢失或额外的字节,这将影响行分隔符的准确度位于。然后,上述方法可能导致文件的其余部分被视为一长行。如果文件足够大,那么如果“行”足够大,则会导致MemoryError
异常。
【讨论】:
我试图通过自己捕获解码异常来寻找替代解决方案。不幸的是(至少在 Python 2 中)解码发生在 在 行结尾被检测到之前,所以你没有得到一致的结果 - 你可能会丢失不止一行,或者你可能会挂永远相同的缓冲区。 @MartijnPietersignore
的问题是它会忽略无效字符而不是整行...所以,我想使用 strict
并捕获异常,以便做得更好粒度错误处理。但是像 OP 一样,我不知道如何用 for 循环来做到这一点......
@flow2k 你不能,因为解码是按文件数据块完成的,而不是每行。有一种解决方法:使用错误处理程序替换错误字符,然后在读取的每一行中查找替换。如果您使用surrogateescape
作为错误处理程序,您甚至可以恢复有问题的字节。我在答案中添加了示例代码。
@MarkRansom 对你有同样的想法,尽管晚了 5 年。
@flow2k surrogateescape 方法给你的是你对解码器说:请继续前进,给我包裹在特殊代码点中的坏数据,并希望最好。我们只相信后面的内容没有严重损坏,我们可以假装行分隔符仍然是行分隔符。以上是关于读取文件时Python中的UnicodeDecodeError,如何忽略错误并跳转到下一行?的主要内容,如果未能解决你的问题,请参考以下文章
读取文件时Python中的UnicodeDecodeError,如何忽略错误并跳转到下一行?
Python Pandas:标记数据时出错。 C 错误:读取 1GB CSV 文件时字符串中的 EOF 开始