【问题标题】:Python truncate lines as they are readPython 在读取行时截断行
【发布时间】:2009-02-08 06:34:52
【问题描述】:

我有一个应用程序,它从文件中读取行并在读取的每一行上运行它的魔法。读取并正确处理该行后,我想从文件中删除该行。已保留已删除行的备份。我想做类似的事情

file = open('myfile.txt', 'rw+')
for line in file:
   processLine(line)
   file.truncate(line)

这似乎是一个简单的问题,但我想把它做对,而不是大量复杂的 seek() 和 tell() 调用。

也许我真正想做的只是从文件中删除特定行。

在这个问题上花了很长时间后,我认为每个人都可能是对的,但这并不是做事的好方法。它只是看起来如此优雅的解决方案。我正在寻找类似于 FIFO 的东西,它可以让我从文件中弹出行。

【问题讨论】:

标签: python file-io


【解决方案1】:

完成后删除所有行:

with open('myfile.txt', 'r+') as file:
    for line in file:
        processLine(line)
    file.truncate(0)

单独删除每一行:

lines = open('myfile.txt').readlines()

for line in lines[::-1]: # process lines in reverse order
    processLine(line)
    del lines[-1]  # remove the [last] line

open('myfile.txt', 'w').writelines(lines)

你可以只留下那些导致异常的行:

import fileinput

for line in fileinput.input(['myfile.txt'], inplace=1):
    try: processLine(line)
    except Exception:
         sys.stdout.write(line) # it prints to 'myfile.txt'

一般来说,正如其他人已经说过的那样,您尝试做的事情是个坏主意。

【讨论】:

  • 这将截断整个文件。
  • @Ryan:是的,它会的。正如你所问的那样。当您完成所有想要删除它们所有。如果不是,请澄清您的问题。
  • 我在帖子中没有说 all 我的问题是截断 A 行
  • 第二种方法不会按预期工作,因为从列表中删除 1 行后,索引将被更改。迭代器 i 将保持原样,但下一个 del 语句将从新修改的列表中删除索引 i
  • @gopi1410:您的原始评论是正确的。如果您需要删除多行,del lines[i] 将无法按预期工作。我已经更新了答案
【解决方案2】:

你不能。在当前文件系统上使用实际的文本文件实现是不可能的。

文本文件是连续的,因为文本文件中的行可以是任意长度。 删除特定行将意味着从该点开始重写整个文件。

假设你有一个包含以下 3 行的文件;

'line1\nline2reallybig\nline3\nlast line'

要删除第二行,您必须移动磁盘中第三行和第四行的位置。唯一的方法是将第三行和第四行存储在某处,截断第二行的文件,然后重写丢失的行。

如果您知道文本文件中每一行的大小,则可以使用 .truncate(line_size * line_number) 在任何位置截断文件,但即便如此,您也必须重写该行之后的所有内容。

【讨论】:

  • 有趣,我并不是很想从文件中删除随机行。那将是非常困难的。更像是在读取文件时截断文件(在开头或结尾)。
  • 这不能回答问题。随机访问删除行不是问题的一部分(既不明确也不暗示)。
  • @Guildenstern 检查问题编辑历史和答案时间戳。
【解决方案3】:

您最好在文件中保留一个索引,这样您就可以从上次停止的地方开始,而不会破坏文件的一部分。像这样的东西会起作用:

try :
    for index, line in enumerate(file) :
        processLine(line)
except :
    # Failed, start from this line number next time.
    print(index)
    raise

【讨论】:

    【解决方案4】:

    在阅读时截断文件似乎有点极端。如果您的脚本有一个不会导致错误的错误怎么办?在这种情况下,您需要在文件开头重新启动。

    让你的脚本打印它中断的行号并让它接受一个行号作为参数,这样你就可以告诉它从哪一行开始处理?

    【讨论】:

    • 该文件只有在该行的操作完成时才会被截断。我也会将此数据写入备份文件...但您并没有真正回答问题。
    【解决方案5】:

    首先,调用操作truncate 可能不是最好的选择。如果我正确理解了这个问题,你想删除文件中当前位置的所有内容。 (我希望 truncate 会剪切从当前位置到文件末尾的所有内容。这就是标准 Python truncate 方法的工作原理,至少如果我正确地用 Google 搜索的话。)

    其次,我不确定在使用for 循环进行迭代时修改文件是否明智。保存处理的行数并在主循环完成后删除它们不是更好吗?文件迭代器支持in-place filtering,这意味着之后删除处理的行应该相当简单。

    附:我不了解 Python,对此持保留态度。

    【讨论】:

      【解决方案6】:

      一个相关的帖子似乎是一个很好的策略,请参阅 How can I run the first process from a list of processes stored in a file and immediately delete the first line as if the file was a queue and I called "pop"?

      我是这样使用的:

        import os;
      
        tasklist_file = open(tasklist_filename, 'rw');  
        first_line = tasklist_file.readline();
        temp = os.system("sed -i -e '1d' " + tasklist_filename); # remove first line from task file;
      

      我不确定它是否适用于 Windows。 在 Mac 上试了一下,果然成功了。

      【讨论】:

      • +1 - 使用 sed 将是最好的肮脏方法
      【解决方案7】:

      这是我用于基于文件的队列的方法。它返回第一行并用其余部分重写文件。完成后返回 None:

      def pop_a_text_line(filename):
          with open(filename,'r') as f:
              S = f.readlines()
          if len(S) > 0:
              pop = S[0]
              with open(filename,'w') as f:
                  f.writelines(S[1:])
          else:
              pop = None
          return pop
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2010-10-02
        • 1970-01-01
        • 2020-02-09
        • 1970-01-01
        • 2021-12-11
        相关资源
        最近更新 更多