我在这里读到,就地编辑文件是不可能的,因为它与操作系统相关并且完全独立于语言。这是为什么?C 标准 fseek() 没有定义就地文件写入吗?
5 回答
您也可以在 Python 中寻找 - 您链接的答案只是指出您不能在文件中间插入ABC
( -> ABXC
)。
如果您先查找然后再写入,则写入的任何数据都会覆盖ABC
文件( -> AXC
)该位置的内容。
问题与操作系统有关,因为它取决于操作系统如何使用其文件系统组织磁盘上的数据。
通常,磁盘被分成固定大小的块来存储文件的内容。当一个字节被覆盖时,只有包含该字节的(整个)块被重写。附加文件时,可能会重写最后一个块,并分配新块并“链接”到文件以存储附加数据的内容。这两个操作在这个方案下是有效的。
但是,当插入或删除某个字节时,通常需要从包含插入/删除更改的第一个块重写文件。
技术上可以减少重写文件所花费的时间。对于插入情况,操作系统可以为插入的数据分配一个新块,将其“链接”到文件,并重写一个或两个相邻块。对于删除情况,操作系统可以减少块中的有效字节数、重写块和/或“重新链接”文件中的块。作为免责声明,我刚才提到的“解决方案”取决于特定文件系统的结构。但是,如果降低重写文件的成本,通常会导致稍后的读取操作受到打击,或者会出现内部碎片。
如果有一天有人发明了一种数据结构,可以在保持读取操作的性能的同时有效地插入/删除磁盘上的数据,也许这个问题就会解决。
关键是文件就像一个数组(只是在磁盘上)。它在磁盘上占据一个固定的位置。
如果必须在数组中间插入一些东西,则必须移出尾随元素,否则将覆盖这些元素。同样的概念适用于文件,您需要移出文件的尾部以在中间插入一些东西。
一个例外是当您需要用相同大小的新块覆盖文件的固定大小块时。然后使用 fseek() 然后覆盖就很好了。
当然,您可以就地编辑文件:
import struct
RECSIZE = struct.calcsize('<i')
fp = open('name.txt', 'r+b')
fp.seek(132 * RECSIZE) # go to record number 132
fp.write(struct.pack('<i', 42))
fp.seek(132 * RECSIZE)
assert struct.unpack('<i', fp.read(RECSIZE)) == 42
这也显示了这样做的最常见原因和局限性。
您可以就地修改文件;如果您要替换的数据与您要替换的数据大小相同(更小并且您只需填充)。如果更大怎么办?好吧,那么修改后的所有内容无论如何都需要重新写入磁盘,对吗?您也不能假设在所有情况下最后都有足够的空间,因此可能需要将文件移动到一个全新的位置。
显然,这使得有效的用例相当罕见。通常,除非您有充分的理由这样做,否则这是不值得的。至于您是否可以访问 Python 中的就地修改 API……我不知道。