在Python中获取文本并重写(更新)文件内容

13

我正在尝试解决这个Rosalind问题,但遇到了问题。我相信我的代码中一切都是正确的,但显然它并没有按照预期运行。我想要删除文件的内容,然后向该文件写入一些文本。程序确实写入了我想要的文本,但它并没有首先删除初始内容。

def ini5(file):
raw = open(file, "r+")
raw2 = (raw.read()).split("\n")
clean = raw2[1::2]
raw.truncate()
for line in clean:
    raw.write(line)
    print(line)

我看到了:

如何在Python脚本中写入内容之前删除文件的内容?

但我的问题仍然存在。我做错了什么?

顺便提一下,这不是一个好的做法——最好为输出创建一个单独的临时文件,并在完成后将其重命名为原始文件;这样,如果您的程序无法完成,就不会破坏输入文件,同时运行在系统上的其他进程始终可以访问其中一个版本,无论是原始版本还是更新后的版本。 - Charles Duffy
3个回答

34

truncate() 截断从当前位置开始的文件内容。根据其文档,强调如下:

将流的大小调整为给定的字节数 (或者如果未指定大小,则调整到当前位置)

在执行了read()方法后,当前位置是文件的结尾。如果想要使用同一文件句柄进行截断和重写操作,需要使用seek(0)将位置移动回开头。

因此:

raw = open(file, "r+")
contents = raw.read().split("\n")
raw.seek(0)                        # <- This is the missing piece
raw.truncate()
raw.write('New contents\n')

(您还可以通过传递raw.truncate(0)来达到同样的效果,但这将使指针——以及未来写入位置的位置——留在文件的起始位置之外,当您从该位置开始写入时,会使您的文件变得稀疏。)


这真的很有帮助,很少有指南提到截断会使光标不在文件开头。 - Val
我认为 seekwritetruncate 可能更加高效。 - Yay295
@Yay295,这取决于你的文件系统;并非每个文件系统都会直接修改数据块。日志记录数据是一种方法。另一个问题是故障情况看起来如何。对于进行取证或恢复的人来说,被截断的数据比被部分覆盖的数据更明显(在我看来),因此如果有选择,我更喜欢它作为故障模式。 - Charles Duffy

10
如果您想完全覆盖文件中的旧数据,则应使用另一种模式打开文件。
应该是:
raw = open(file, "w") # or "wb"

要解决您的问题,首先请阅读文件内容:

with open(file, "r") as f: # or "rb"
    file_data = f.read()
# And then:
raw = open(file, "w")

然后使用write模式打开它。这样,您不会将文本附加到文件中,而只会将数据写入其中。

在这里阅读有关模式文件的信息


OP想在覆盖之前执行读取操作--否则,是的,他们只会使用w,但它不适用于该目的。如果您建议他们关闭读取句柄,然后创建第二个写入句柄,那么这是可行的,但您可能需要更明确地说明。 - Charles Duffy
1
为了简单起见点赞。如果可以的话,我会避免打开读/写句柄。尽管我怀疑重复使用相同的句柄会更快。 - Jean-François Fabre
好的,所以我已经让它按照我想要的方式运行了。谢谢大家!不过,为了澄清一下 -- 要提取内容(比如字符串信息),我必须以读模式打开文件,然而要编辑实际文件,我必须以写模式打开它。要同时做到这两点,我必须先以读模式打开文件,然后再以写模式打开(不能使用"r+")? - Tare Gaskin
不完全是这样,你可以使用“r+”。在你的特定情况下,两者都可以。只是为了简单起见将它们分开了。 - Ofer Arial
1
@TareGaskin,r+当然是可以使用的--我的答案描述了如何在写入之前截断文件并回到文件开头。因此,您有两个选择,具有不同行为的两种不同解决方案--哪一个更合适取决于您的用例。打开一个单独的文件句柄意味着您可能会得到一个与您读取的不同inode来进行写入,如果您的目录项在您下面发生了更改,所以这些都是非常不同的解决方案,各自具有自己的语义。 - Charles Duffy

-1

最好的方法是:

with open('shoot.txt', 'r+') as file:
    text = file.read()  # get text for later use

    file.truncate(0)  # Note the "0" param
    file.seek(0)

    file.write("new text")

我在truncate(0)中漏掉了零。抱歉。是的,它有效。顺便说一下,除非你的答案被编辑,否则我无法取消我的负评。 - Ben Slade

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接