读取文件时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 中)解码发生在 行结尾被检测到之前,所以你没有得到一致的结果 - 你可能会丢失不止一行,或者你可能会挂永远相同的缓冲区。 @MartijnPieters ignore 的问题是它会忽略无效字符而不是整行...所以,我想使用 strict 并捕获异常,以便做得更好粒度错误处理。但是像 OP 一样,我不知道如何用 for 循环来做到这一点...... @flow2k 你不能,因为解码是按文件数据块完成的,而不是每行。有一种解决方法:使用错误处理程序替换错误字符,然后在读取的每一行中查找替换。如果您使用surrogateescape 作为错误处理程序,您甚至可以恢复有问题的字节。我在答案中添加了示例代码。 @MarkRansom 对你有同样的想法,尽管晚了 5 年。 @flow2k surrogateescape 方法给你的是你对解码器说:请继续前进,给我包裹在特殊代码点中的坏数据,并希望最好。我们只相信后面的内容没有严重损坏,我们可以假装行分隔符仍然是行分隔符。

以上是关于读取文件时Python中的UnicodeDecodeError,如何忽略错误并跳转到下一行?的主要内容,如果未能解决你的问题,请参考以下文章

读取文件时Python中的UnicodeDecodeError,如何忽略错误并跳转到下一行?

Python Pandas:标记数据时出错。 C 错误:读取 1GB CSV 文件时字符串中的 EOF 开始

在 Python 中读取 csv 文件时获取“字符串中的换行符”?

读取文件直到python中的特定行

在使用 python 写入时从日志文件中读取

Python:读取文件时如何忽略两个特定单词之间的文本?