Python读取文件时发生UnicodeDecodeError,如何忽略错误并跳到下一行?
我需要把一个文本文件读入到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;
那么当遇到错误时,循环就会结束,我就无法读取文件的剩余部分。我想跳过导致错误的那一行,继续往下读。如果可以的话,我希望不对输入文件做任何更改。
有没有什么办法可以做到这一点?非常感谢。
1 个回答
你的文件似乎没有使用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)]
例如:
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
异常,尤其是当这“行”非常大的时候。