Python 在任意位置就地写入文件

Python in-place write to file at arbitrary position

我正在尝试在 python 中就地编辑文本文件。它非常大(因此无法将其加载到内存中)。我打算替换我在里面找到的逐字节字符串。

with f as open("filename.txt", "r+b"):
    if f.read(8) == "01234567":
        f.seek(-8, 1)
        f.write("87654321")

但是,当我尝试 write() 操作时,它会添加到文件末尾:

>>> n.read()
'sdf'
>>> n.read(1)
''
>>> n.seek(0,0)
>>> n.read(1)
's'
>>> n.read(1)
'd'
>>> n.write("sdf")
>>> n.read(1)
''
>>> n.seek(0,0)
>>> n.read()
'sdfsdf'
`

我希望结果是 sdsdf

原始的 ANSI / ISO C 标准要求在将读写模式流从读取模式切换到写入模式时进行寻道操作,反之亦然。此限制仍然存在,例如 n1570 包含此文本:

When a file is opened with update mode ('+' as the second or third character in the above list of mode argument values), both input and output may be performed on the associated stream. However, output shall not be directly followed by input without an intervening call to the fflush function or to a file positioning function (fseek, fsetpos, or rewind), and input shall not be directly followed by output without an intervening call to a file positioning function, unless the input operation encounters end-of-file. Opening (or creating) a text file with update mode may instead open (or create) a binary stream in some implementations.

无论出于何种原因,此限制已导入 Python、1,尽管 Python 包装器可能会自动处理它。

对于它的价值,最初 ANSI C 限制的原因是在许多基于 Unix 的系统上发现的低预算实现:他们为每个流保留了 "current byte count" 和 "current pointer".如果宏化 getcputc 操作必须调用底层实现,则当前字节数为 0,底层实现可以检查流是否以更新模式打开并根据需要切换它。但是一旦你成功获得了一个字符,计数器就会保存可以继续从底层流中读取的字符数;一旦你成功写入一个字符,计数器将保存允许添加字符的缓冲区位置的数量。

这意味着,如果您成功执行 getc 填充内部缓冲区,但后面跟着 putcputc 中的 "written" 字符将简单地覆盖缓冲数据。如果您有一个成功的 putc 但随后执行的 getc 执行不力,您会看到缓冲区中未设置的值。

这个问题很容易解决(只需提供单独的输入和输出计数器,其中之一始终为零,并且还具有为模式切换实现缓冲区填充检查的功能)。


1需要引用:-)

您可以查看以下代码的区别:

>>> f = open("file.txt", "r+b")
>>> f.seek(2)
>>> f.write("sdf")
>>> f.seek(0)
>>> f.read()
'sdsdf'


>>> f = open("file.txt", "r+b")
>>> f.read(1)
's'
>>> f.read(1)
'd'
>>> f.write("sdf")
>>> f.seek(0)
>>> f.read()
'sdfsdf'

.write的指针原来在文件的末尾。只有 .seek() 会改变它的位置,但 .read() 不会。所以你必须在写入字节之前调用 .seek() 。以下代码运行良好:

>>> f = open("file.txt", "r+b")
>>> f.read(1)
's'
>>> f.read(1)
'd'
>>> f.seek(2)
>>> f.write("sdf")
>>> f.seek(0)
>>> f.read()
'sdsdf'