csv 大文件删除行python

vnzz0bqm  于 2023-02-14  发布在  Python
关注(0)|答案(2)|浏览(150)

需要一些帮助与用例。我有两个文件一个是约9GB(测试数据)和其他42MB(master_data). test_data包含多列数据,其中一列(即#7)包含电子邮件地址。master_data是我的主数据文件,其中只有一列是电子邮件地址。我试图实现的是将master_data文件中的电子邮件与test_中的电子邮件进行比较如果数据匹配,整行将被删除。我需要一个有效的方法来实现相同的。
下面的代码是为了实现而编写的,但我一直在删除master_data文件中的行,但不确定这是否是实现此要求的有效方法。

import csv
import time
# open the file in read mode
filename = open('master_data.csv', 'r')

# creating dictreader object
file = csv.DictReader(filename)

# creating empty lists
email = []

# iterating over each row and append
# values to empty list
for col in file:
    email.append(col['EMAIL'])

# printing lists
print('Email:', email)

datafile = open('test_data.csv', 'r+')
for line in datafile:
    #print(line)
#    str1,id=line.split(',')
    split_line=line.split(',')
    str1=split_line[7] # Whatever columns
    id1=split_line[0]
    for w in email:
        print(w)
        print(str1)
        #time.sleep(2.4)
        if w in str1:
            print(id1)
            datafile.remove(id1)
lndjwyie

lndjwyie1#

您可以加载主文件并将电子邮件存储在一个dict中,然后当您迭代test的行时,您可以检查一行的电子邮件是否在该(主)dict中。
鉴于这些CSV:

  • test.csv:
Col1,Col2,Col3,Col4,Col5,Col6,Col7
1,,,,,,foo@mail.com
2,,,,,,bar@mail.com
3,,,,,,baz@mail.com
4,,,,,,dog@mail.com
5,,,,,,foo@mail.com
  • master.csv:
Col1
foo@mail.com
cat@mail.com
dog@mail.com

当我跑步时:

import csv

emails: dict[str, None] = {}

with open("master.csv", newline="") as f:
    reader = csv.reader(f)
    next(reader)  # skip header

    for row in reader:
        emails[row[0]] = None

out_line = "{:<20} {:>8}"

with open("test.csv", newline="") as f:
    reader = csv.reader(f)
    next(reader)  # skip header

    print(out_line.format("Email", "Test ID"))
    for row in reader:
        if row[6] in emails:
            print(out_line.format(row[6], row[0]))

我得到:

Email                 Test ID
foo@mail.com                1
dog@mail.com                4
foo@mail.com                5

这证明了你可以在阅读测试的同时阅读大师的邮件并与之进行比较。
正如其他人所指出的,实际上从文件中删除任何内容都是困难的;创建一个新文件并排除(过滤掉)您不想要的内容要容易得多:

f_in = open("test.csv", newline="")
reader = csv.reader(f_in)

f_out = open("output.csv", "w", newline="")
writer = csv.writer(f_out)

for row in reader:
    if row[6] in emails:
        continue

    writer.writerow(row)

f_in.close()
f_out.close()

迭代CSV阅读器并使用CSV编写器写出是转换CSV的一种非常有效的方法(在本例中为test.csv → output.csv):在循环的每一步中只需要存储row的内存。
当我运行它时,像以前一样填充电子邮件dict后,我的输出.csv如下所示:

Col1,Col2,Col3,Col4,Col5,Col6,Col7
2,,,,,,bar@mail.com
3,,,,,,baz@mail.com

对于您的情况的实际性能,我为master模拟了一个42 MB的CSV文件-1.35M行32个字符长的十六进制字符串。阅读这些1.35M唯一字符串并将其保存在dict中只需要不到1秒的实时时间,并使用176 MB的RAM(在我的M1 Macbook Air上,带有双通道SSD)。
此外,我建议每次需要读/写CSV时都使用csv模块。无论CSV看起来多么简单,使用csv读/写器都将是100%正确的,与尝试手动拆分或连接逗号相比,几乎没有开销。

tyg4sfes

tyg4sfes2#

从文件中删除行是很困难的。写一个新文件,过滤掉行要容易得多。把你现有的电子邮件放在一个集合中,方便查找,写入一个临时文件,完成后重命名。这也有一个好处,如果沿着出了问题,你不会丢失数据。
你需要“规范化”电子邮件。大多数电子邮件系统不区分大小写,并且忽略地址中的句点。地址也可以包含额外的名称信息,如John Doe <j.doe@Gmail.com>。编写一个函数,将地址转换为一种格式,并将其用于两个文件。

import csv
import os
import email.utils

def email_normalize(val):
    # discard optional full name. lower case, remove '.' in local name
    _, addr = email.utils.parseaddr(val) 
    local, domain = addr.lower().split('@', 1)
    local = local.replace('.', '')
    return f'{local}@{domain)'

# create set of user emails to keep
with open('master_data.csv', newline='') as file:
    emails = set(email_normalize(row[0]) for row in csv.reader(file))

with open('test_data.csv', newline='') as infile, \
        open('test_data.csv.tmp', 'w', newline='') as outfile:
    reader = csv.reader(infile)
    writer = csv.writer(outfile)
    writer.writerow(next(reader)) # write header
    writer.writerows(row for row in reader
        if email_normalize(row[7]) not in emails) # email is column #7
    del reader, writer

os.rename('test_data.csv', 'test_data.csv.deleteme')
os.rename('test_data.csv.tmp', 'test_data.csv')
os.remove('test_data.csv.deleteme')

相关问题