2013-04-29 177 views
8

我试图比较两个csv文件(fileA和fileB),并从fileA中删除fileA中找不到的任何行。我希望能够在不创建第三个文件的情况下执行此操作。我以为我可以使用csv编写器模块来做到这一点,但现在我第二次猜测自己。如何在python中删除行CSV

目前,我使用下面的代码从文件B记录我的比较数据:

removal_list = set() 
with open('fileB', 'rb') as file_b: 
    reader1 = csv.reader(file_b) 
    next(reader1) 
    for row in reader1: 
     removal_list.add((row[0], row[2])) 

这是我坚持,不知道如何删除行:

with open('fileA', 'ab') as file_a: 
    with open('fileB', 'rb') as file_b: 
     writer = csv.writer(file_a) 
      reader2 = csv.reader(file_b) 
      next(reader2) 
      for row in reader2: 
       if (row[0], row[2]) not in removal_list: 
       # If row was not present in file B, Delete it from file A. 
       #stuck here: writer.<HowDoIRemoveRow>(row) 
+1

['sqlite'](http://docs.python.org/2/library/sqlite3.html)是一个基于平面文件的数据库,其驱动程序包含在现代版本的Python中。考虑到你正在尝试做什么,这可能是一个更好的选择。 – 2013-04-29 05:14:49

+0

对不起这个愚蠢的问题,但这会创建一个fileB的精确副本,不是吗? – 2016-07-05 14:40:14

回答

7

该解决方案使用fileinputinplace=True,这将写入到一个临时文件,然后自动在年底到您的文件重命名为名称。你不能从文件中删除行,但你可以只用你想要的那个来重写它。

如果关键字参数inplace=1被传递给fileinput.input()或到FileInput构造函数,则文件被移动到备份文件和标准输出被引导至输入文件(如果具有相同名称的文件,作为所述备份文件已经存在,它会被静静地取代)。这使得可以编写一个过滤器来重写其输入文件。

的fileA

h1,h2,h3 
a,b,c 
d,e,f 
g,h,i 
j,k,l 

FILEB

h1,h2,h3 
a,b,c 
1,2,3 
g,h,i 
4,5,6 

import fileinput, sys, csv 

with open('fileB', 'rb') as file_b: 
    r = csv.reader(file_b) 
    next(r) #skip header 
    seen = {(row[0], row[2]) for row in r} 

f = fileinput.input('fileA', inplace=True) # sys.stdout is redirected to the file 
print next(f), # write header as first line 

w = csv.writer(sys.stdout) 
for row in csv.reader(f): 
    if (row[0], row[2]) in seen: # write it if it's in B 
     w.writerow(row) 

的fileA

h1,h2,h3 
a,b,c  
g,h,i 
+0

解释中没有提到的微妙改进:该代码使用了一套更为优化的数据结构来回答“数据是否存在?”比列表(每次都必须重复)。 – 2013-04-29 14:06:39

+0

@大卫欧普也使用了一套,虽然 – jamylak 2013-04-29 14:08:06

+0

D'oh。他/她显然做到了。那么,小小的建议 - 不要称之为“清单”,或者像我这样的骨头人会对变量的类型感到困惑。 =) – 2013-04-29 14:19:09

3

CSV不是数据库格式。它是作为一个整体来读写的。你不能删除中间的行。因此,在不创建第三个文件的情况下完成此操作的唯一方法是将文件完全读入内存中,然后将其写出,而不会出现违规行。

但总的来说,最好使用第三个文件。

3

正如Lennart所描述的那样,您不能在遍历它时就地修改CSV文件。

如果你真的反对创建第三个文件,你可能想使用一个字符串缓冲区与StringIO,这个想法是你在内存中建立新的文件A的内容。在脚本的末尾,您可以通过文件A.写缓冲区的内容

from cStringIO import StringIO 


with open('fileB', 'rb') as file_b: 
    new_a_buf = StringIO() 
    writer = csv.writer(new_a_buf) 
    reader2 = csv.reader(file_b) 
    next(reader2) 
    for row in reader2: 
     if (row[0], row[2]) not in removal_list: 
      writer.writerow(row) 

# At this point, the contents (new_a_buf) exist in memory 
with open('fileA', 'wb') as file_a: 
    file_a.write(new_a_buf.getvalue()) 
+0

这里有个小心点:如果输入文件很大,可能会耗尽系统的可用内存。 – 2013-04-29 05:13:08

+0

你也许只需要写一个不同的文件并在最后重命名它,这就是我的解决方案 – jamylak 2013-04-29 05:13:14

+0

@jamylak,我完全同意你的看法。这正是我在这种情况下所要做的。我只是觉得这在技术上符合提问者的要求是有用的。 – 2013-04-29 13:57:12