【问题标题】:Fastest Way to Delete a Line from Large File in Python在 Python 中从大文件中删除一行的最快方法
【发布时间】:2011-01-20 17:27:15
【问题描述】:

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

最终,一旦我对这个过程感到满意,我就会完全自动化它。不过现在,假设我是手动运行的。

从这个大文件中删除特定行的最快(就执行时间而言)方法是什么?我想用 Python 来做这件事……但会对其他例子持开放态度。该行可能在文件中的任意位置

如果是 Python,假设如下接口:

def removeLine(filename, lineno):

谢谢,

-aj

【问题讨论】:

  • 使用 grep -v 可能比使用 Python 更快
  • 你要删除哪一行?您将如何识别它?这个问题的答案可能会对战略产生重大影响。
  • 脚本解决方案绝对必要吗?大文本文件查看器 (swiftgear.com/ltfviewer/features.html) 应该能够处理该文件,您可以使用正则表达式搜索正确的行。
  • 一个合适的文本编辑器(例如 gvim)对于较长的文本文件应该不会有太大的麻烦。 11GB 并不少见……
  • 修改了问题以提供有关要求的更多详细信息,谢谢。

标签: python optimization


【解决方案1】:

您可以同时为同一个文件拥有两个文件对象(一个用于读取,一个用于写入):

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

【讨论】:

  • 没有 args 的截断做什么? python文档不是很清楚。
  • @JamesMcMahon:文档到底有什么不清楚的地方? “截断文件的大小。如果存在可选的大小参数,则文件将被(最多)截断为该大小。大小默认为当前位置。”
  • 虽然我赞成这个问题给出一些初步的想法,但我写了一个正确使用 RAII(“with”)的示例,并为搜索字符串添加了一个附加变体。
  • 'frw.writelines(chars)' 行至少在 Python3 中应该是 'frw.write(chars)'
  • 是什么阻止了写入对象 frw 与读取对象冲突?
【解决方案2】:

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

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)

如果其他程序可以改成输出fileoffset而不是行号,可以直接将offset赋值给p,不用for循环

【讨论】:

【解决方案3】:

据我所知,你不能只用 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()

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

【讨论】:

  • 只是一个小注释,在for循环中使用enumerate()来计算迭代次数通常更方便,如:for ind, line in enumerate(f):
【解决方案4】:

如果行是可变长度的,那么我认为没有比逐行读取文件并写出所有行更好的算法了,除了你不想要的行。

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

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

【讨论】:

  • @Dancrumb - 感谢您的想法。不幸的是,行/记录是可变长度的。
【解决方案5】:

更新:按照发帖者在评论中的要求使用 sed 的解决方案。

例如删除文件的第二行:

sed '2d' input.txt

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

【讨论】:

    【解决方案6】:
    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)
    

    【讨论】:

      【解决方案7】:

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

      需要注意的是,当您读取一个块时,您需要处理您读取的最后一行(可能是部分行),并将其添加到您读取的下一个块中。

      【讨论】:

        【解决方案8】:

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

        $ awk 'NR!=10' file > newfile
        

        【讨论】:

          【解决方案9】:

          我将根据查找因素(行号或搜索字符串)提供两种选择:

          行号

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

          【讨论】:

            猜你喜欢
            • 2015-03-23
            • 2011-04-14
            • 1970-01-01
            • 2010-09-16
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2010-10-17
            相关资源
            最近更新 更多