Python: 改变文件某行某处的几个字符

Python: Change a few characters at a certain location in a certain line of a file

我知道有很多关于编辑文件行的问题,但我的问题很具体,两天后我找不到 question/answer 在这里找到它。

问题

如何将文件中特定行的几个(连续)字符 s1 替换为另外几个字符 s2以下条件?

  1. 行号始终相同。 (5 号)
  2. s1前面的那一行总是一样的。 (因此具有恒定长度 = 18)
  3. s1 前面的行部分不会出现在文件的其他任何地方。
  4. s1 和 s2 都不是常数,甚至可以有不同的长度。
  5. s1 和 s2 都可能出现在文件的其他任何地方。
  6. 文件可能很长,所以我不想将整个文件加载到内存中。
  7. 同6的原因,我想避免将文件内容复制到新文件中。我只是更改了几个字符,所以重写整个文件的开销会很大,不是吗?
  8. 我正在使用 Python 3.X.

到目前为止,我发现的大多数类似方法都不符合 6. 或 7。我发现 this(使用 r+ 打开文件并在 s1 之前执行 write(s2) ),但它对我不起作用,因为 4。是否有可能在 Python 中实现我想要的,或者我是否必须以某种方式复制我的文件并最终沿途修改该行?

背景

我有一个文本文件,其中包含几行元数据,后面可能有大量数据集。元数据包含一行 No. of patterns : n 而 n 是文件中数据集的数量。除其他事项外,我的脚本应该能够通过附加数据集本身并更新 n 来将附加数据集附加到现有文件。 我想通过我的脚本 generated/extended 这个文件的设计不是我发明的,所以我不能改变它。该文件将用作另一个不是我发明的应用程序的输入 - JavaNNS。

The answer you linked

you can only extend and truncate a file at the end, not at the head

有了这个限制,python 只是反映了我们称之为 'file system' 的数据存储抽象所施加的限制。所有程序,无论是何种编程语言,在使用文件系统时都受此约束。有些只是通过在后台重写完整的文件来向用户隐藏这一事实。

如果由于文件的大小导致更新文件时出现性能问题,那么这确实是原始文件格式的问题,即使您不是应该为此负责的人:文件格式似乎不适合更改模式数量的文件的就地更新。

如何避免(重新)写入大量数据

管道

如果将使用更新文件 (JavaNNS) 的程序在标准输入上接受文件内容,请考虑将元数据和模式保存在单独的文件中。像这样,您可以附加模式文件并且只重写(希望很小的)元数据文件。然后,只需一次调用将两个文件通过管道传输到 JavaNNS 中:

cat metadata.txt patterns.txt | JavaNNS

如果 JavaNNS 在标准输入上接受所需的文件内容但坚持自己打开文件,您可能仍然可以使用 named pipe 并传递它作为要打开的文件。 (如果 JavaNNS 对文件进行随机访问而不是仅仅读取和查找,这可能不起作用。)

填充

如果您要多次附加到文件并且文件格式足够灵活以允许进行一些填充,那么只需填充一些 space 用于 n 并可能增加数量未来写入的数字。像这样,你只需要在填充不够大的时候完全重写文件。

您不能就地编辑,只能将 s1 更改为 s2,因为它们的长度可能不同。您将需要写出文件的其余部分,使用替换文件会更安全。

如果保证 s1 和 s2 的长度相同,那么您可以就地完成,例如该值被填充到 s1/s2:

的最大大小
with open('<file>', 'r+') as f:
    for line_no, line in enumerate(f):
        if line_no == 5:      # read 5 lines
            f.seek(18, 1)     # jump forward 18 characters
            f.write("{: 8d}".format(s2))  # overwrite with padded s2 (int)
            break

不同的长度需要不同的文件:

with open('<file>', 'r') as r:
    with open('<file-new>', 'w+') as w: 
        for line_no, line in enumerate(r):
            if line_no == 5:
                w.write(line[:18] + str(s2) + line[18+len(s1):])
            else:
                w.write(line)