在Python中从大文件中删除行的最快方法

时间:2010-02-24 20:51:01

标签: python optimization

我在Linux系统上使用非常大的(~11GB)文本文件。我正在通过检查文件错误的程序运行它。一旦发现错误,我需要修复该行或完全删除该行。然后重复......

最终,一旦我对这个过程感到满意,我会完全自动完成它。但是现在,让我们假设我手动运行它。

从这个大文件中删除特定行的最快(在执行时间方面)是什么?我想在Python中做到这一点......但是会对其他例子持开放态度。该行可能是文件中的任何地方

如果是Python,请假设以下界面:

def removeLine(filename, lineno):

谢谢,

-AJ

9 个答案:

答案 0 :(得分:13)

同一个文件可以有两个文件对象(一个用于阅读,一个用于写入):

def removeLine(filename, lineno):
    fro = open(filename, "rb")

    current_line = 0
    while current_line < lineno:
        fro.readline()
        current_line += 1

    seekpoint = fro.tell()
    frw = open(filename, "r+b")
    frw.seek(seekpoint, 0)

    # read the line we want to discard
    fro.readline()

    # now move the rest of the lines in the file 
    # one line back 
    chars = fro.readline()
    while chars:
        frw.writelines(chars)
        chars = fro.readline()

    fro.close()
    frw.truncate()
    frw.close()

答案 1 :(得分:7)

修改文件到位,违规行被替换为空格,因此文件的其余部分不需要在磁盘上进行随机播放。如果修复不长于您要替换的行,您还可以“修复”该行

import os
from mmap import mmap
def removeLine(filename, lineno):
    f=os.open(filename, os.O_RDWR)
    m=mmap(f,0)
    p=0
    for i in range(lineno-1):
        p=m.find('\n',p)+1
    q=m.find('\n',p)
    m[p:q] = ' '*(q-p)
    os.close(f)

如果可以更改其他程序以输出文件偏移而不是行号,则可以直接将偏移量分配给p并且不使用for循环

答案 2 :(得分:1)

据我所知,你不能只用python打开一个txt文件并删除一行。您必须创建一个新文件并将除此行之外的所有内容移动到该文件中。如果你知道具体的行,那么你会做这样的事情:

f = open('in.txt')
fo = open('out.txt','w')

ind = 1
for line in f:
    if ind != linenumtoremove:
        fo.write(line)
    ind += 1

f.close()
fo.close()

您当然可以检查该行的内容,以确定是否要保留该行。我还建议如果您要删除/更改完整行列表,以便在一次传递文件中执行所有这些更改。

答案 3 :(得分:1)

如果这些行是可变长度的,那么我不相信有一个比逐行读取文件和写出所有行更好的算法,除了你不想要的那些行。

您可以通过检查某些条件来识别这些行,或者通过保持读取行的运行计数并禁止写入您不想要的行。

如果行是固定长度而你想删除特定的行号,那么你可以使用seek来移动文件指针......我怀疑你是那么幸运。

答案 4 :(得分:1)

更新:解决方案中海报要求使用sed解决方案。

删除例如第二行文件:

sed '2d' input.txt

使用-i开关进行编辑。警告:这是一种破坏性操作。请阅读此命令的帮助,以获取有关如何自动进行备份的信息。

答案 5 :(得分:0)

def removeLine(filename, lineno):
    in = open(filename)
    out = open(filename + ".new", "w")
    for i, l in enumerate(in, 1):
        if i != lineno:
            out.write(l)
    in.close()
    out.close()
    os.rename(filename + ".new", filename)

答案 6 :(得分:0)

我认为这里提出的问题有点类似,如果不完全相同。逐行读取(和写入)很慢,但是您可以一次读取更大的块到内存中,逐行跳过您不想要的行,然后将其作为单个块写入新文件。重复直到完成。最后用新文件替换原始文件。

需要注意的是,当你读入一个块时,你需要处理你读取的最后一个可能是部分的行,然后将它添加到你读到的下一个块中。

答案 7 :(得分:0)

@OP,如果你可以使用awk,例如假设行号为10

$ awk 'NR!=10' file > newfile

答案 8 :(得分:0)

我将根据查找因子(行号或搜索字符串)提供两种备选方案:

行号

def removeLine2(filename, lineNumber):
    with open(filename, 'r+') as outputFile:
        with open(filename, 'r') as inputFile:

            currentLineNumber = 0 
            while currentLineNumber < lineNumber:
                inputFile.readline()
                currentLineNumber += 1

            seekPosition = inputFile.tell()
            outputFile.seek(seekPosition, 0)

            inputFile.readline()

            currentLine = inputFile.readline()
            while currentLine:
                outputFile.writelines(currentLine)
                currentLine = inputFile.readline()

        outputFile.truncate()

字符串

def removeLine(filename, key):
    with open(filename, 'r+') as outputFile:
        with open(filename, 'r') as inputFile:
            seekPosition = 0 
            currentLine = inputFile.readline()
            while not currentLine.strip().startswith('"%s"' % key):
                seekPosition = inputFile.tell()
                currentLine = inputFile.readline()

            outputFile.seek(seekPosition, 0)

            currentLine = inputFile.readline()
            while currentLine:
                outputFile.writelines(currentLine)
                currentLine = inputFile.readline()

        outputFile.truncate()