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

时间:2014-07-07 17:49:26

标签: python python-3.x utf-8

我必须将文本文件读入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循环结束,我无法读取剩余的文件。我想跳过导致错误的行并继续。如果可能的话,我宁愿不对输入文件进行任何更改。

有没有办法做到这一点? 非常感谢你。

1 个答案:

答案 0 :(得分:38)

您的文件似乎没有使用UTF-8编码。打开文件时使用正确的编解码器非常重要。

告诉open()如何使用errors关键字处理解码错误:

  

errors 是一个可选字符串,用于指定如何处理编码和解码错误 - 这不能在二进制模式下使用。虽然已向codecs.register_error()注册的任何错误处理名称也有效,但可以使用各种标准错误处理程序。标准名称是:

     
      如果存在编码错误,
  • 'strict'会引发ValueError异常。默认值None具有相同的效果。
  •   
  • 'ignore'忽略错误。请注意,忽略编码错误可能会导致数据丢失。
  •   
  • 'replace'会导致替换标记(例如'?')插入格式错误的数据。
  •   
  • 'surrogateescape'将表示任何不正确的字节,作为Unicode专用区中的代码点,范围从U + DC80到U + DCFF。当写入数据时使用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)]

E.g。

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异常。足够大了。