【问题标题】:How to Delete Rows CSV in python如何在 python 中删除行 CSV
【发布时间】:2013-04-22 16:29:56
【问题描述】:

我正在尝试比较两个 csv 文件(fileA 和 fileB),并从 fileA 中删除在 fileB 中找不到的所有行。我希望能够在不创建第三个文件的情况下做到这一点。我以为我可以使用 csv writer 模块来做到这一点,但现在我在猜测自己。

目前,我正在使用以下代码从文件 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)

【问题讨论】:

  • sqlite 是一个基于平面文件的数据库,它的驱动程序包含在现代版本的 Python 中。考虑到您正在尝试做的事情,这可能是一个更好的选择。
  • 抱歉这个愚蠢的问题,但这会创建文件B的精确副本,不是吗?

标签: python csv python-2.7 module delete-row


【解决方案1】:

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

但一般来说最好使用第三个文件。

【讨论】:

    【解决方案2】:

    正如 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())
    

    【讨论】:

    • 这里要注意一点:如果您的输入文件很大,您可能会耗尽系统的可用内存。
    • 你也可以只写一个不同的文件并在最后重命名它,这就是我的解决方案
    • @jamylak,我完全同意你的看法。这正是我在这种情况下会做的。我只是认为这会很有用,因为它在技术上符合提问者的要求。
    【解决方案3】:

    此解决方案使用fileinputinplace=True,它会写入一个临时文件,然后在末尾自动将其重命名为您的文件名。您不能从文件中删除行,但您可以只用您想要的行重写它。

    如果关键字参数inplace=1 被传递给fileinput.input()FileInput 构造函数,则文件被移动到备份文件,标准输出被定向到输入文件(如果文件与备份文件已经存在,它将被静默替换)。这使得编写一个过滤器来重写其输入文件成为可能。

    文件A

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

    文件B

    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)
    

    文件A

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

    【讨论】:

    • 解释中未提及的细微改进:此代码使用一个集合,一个更优化的数据结构来回答“此数据存在吗?”而不是一个列表(每次都必须迭代)。
    • @David Op 也使用了一个集合
    • 哦。他/她显然做到了。好吧,一点建议 - 不要将其称为删除“列表”,否则像我这样的愚蠢的人会对变量的类型感到困惑。 =)
    • 什么版本的python?我不相信这种语法与 2.4 兼容
    • @justin 您将其标记为 2.7?你可以改用set((row[0], row[1]) for row in r)
    猜你喜欢
    • 2019-08-07
    • 1970-01-01
    • 1970-01-01
    • 2021-12-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-06-19
    相关资源
    最近更新 更多