我有一个包含大量电子邮件的.csv文件,每个电子邮件都在一个单独的行上。我试图删除任何包含非ascii字符的电子邮件。这就是我想要的:
def is_ascii(s):
return all(ord(c) < 128 for c in s)
if __name__ == "__main__":
with open('emails.csv') as csv_file:
for line in csv_file:
if(is_ascii(line)):
with open('result.csv', 'a') as output_file:
output_file.write(line)
它一直给我一个错误:
UnicodeDecodeError: 'utf-8' codec can't decode byte 0x83 in position 5012: invalid start byte
答案 0 :(得分:0)
问题是你不知道非ASCII电子邮件的编码是什么,所以你只想跳过它们。
但您的代码正在尝试使用默认编码对其进行解码,然后决定是否跳过它们。这就是在文本模式下打开文件的意思,如下所示:
with open('emails.csv') as csv_file:
for line in csv_file:
由于默认编码是UTF-8,因此只要遇到某些其他不兼容UTF-8的字符集编码的内容,就会出错。
将此更改为最简单的方法是以二进制模式打开文件。然后你只能解码你决定保留的行:
with open('emails.csv', 'rb') as csv_file:
for line in csv_file:
if(is_ascii(line)):
line = line.decode('ascii')
with open('result.csv', 'a') as output_file:
output_file.write(line)
...或者只是通过以二进制模式打开输出文件来保持整个字节:
with open('emails.csv', 'rb') as csv_file:
for line in csv_file:
if(is_ascii(line)):
with open('result.csv', 'ab') as output_file:
output_file.write(line)
无论哪种方式,您都必须更改isascii
函数,因为bytes
是一个0-255的整数序列,而不是一系列字符,所以你不能(和不需要)致电ord
:
def is_ascii(s):
return all(c < 128 for c in s)
存在潜在问题。我认为你会好的,但你应该考虑一下(并测试你需要测试的任何东西)。虽然文本模式文件对象自动处理非Unix换行符,但二进制模式文件却没有。
如果您以某种方式拥有上个世纪的经典Mac(pre-OS X)文件,并且\r
结尾,则您的代码将无效。 \r
根本不会被视为换行符,因此整个文件看起来就像一条巨大的行。如果您不希望有任何此类文件,我不会担心。
但是如果您拥有的唯一非Unix文件是Windows(或DOS),\r\n
,那么你会没事的。 \r
将被视为行的一部分而不是换行的一部分,但这对您的代码(ord('\r') < 128
无关紧要,除此之外,您所做的只是编写整个行一次性的字节行数),所以事情就会起作用。