为什么在Python中以"w"模式打开文件时要截断文件?

60

我正在阅读 Zed Shaw 的 Python 书籍。目前我正在学习有关打开和读取文件的章节。我想知道,既然我们已经用 'w' 模式打开了文件,为什么还需要执行 truncate 操作呢?

print "Opening the file..."
target = open(filename, 'w')

print "Truncating the file. Goodbye!"
target.truncate()

18
展示它能够被完成。 - Ignacio Vazquez-Abrams
1
该方法在只读模式下不起作用。 - Varun Moghe
9个回答

59

这是多余的,正如你所注意到的,以写模式打开文件将覆盖该文件。更多信息请参见Python文档中的输入和输出部分。


这并不是无用的,有时候当我要写入比文件内容更大的文件时,我需要在之后截断文件以去除尾部垃圾,例如使用 JSON。 - NoBlockhit

36

所以 Zed Shaw 对已经被截断的文件调用了 truncate() 方法。好吧,那很无意义。他为什么这样做呢?谁知道呢?问问他吧!

也许他这样做是为了展示该方法的存在?可能吧,但那太愚蠢了,因为作为一个程序员,我在15年的职业生涯中从未需要过截断文件,所以它在一本新手书中没有任何用处。

也许他之所以这样做是因为他认为他必须截断文件,而他并不知道这是无意义的?

也许他故意这样做来困惑新手?这符合他的一般行事方式,似乎他的唯一目的就是毫无理由地刻意惹怒人们。

更新:现在清楚了他这样做的原因。在后续版本中,他将此问题列为章节中的“常见问题”,并告诉你去阅读文档。因此,它的目的是为了:

  1. 教你阅读文档。
  2. 在你复制粘贴代码之前,理解你从其他地方复制粘贴的代码的每个部分。

你可以争论这是否是一种好的教学方式,我不知道。

在 SO 上,“帮助我不理解 Zed Shaw 的书”问题的数量已经减少了,所以我不能说它比其他任何书更糟糕,这可能意味着它比许多书更好。 :-)


@Lennart Regebro 推荐使用 Django 入门指南这本书,而 Two scoops of Django 则建议使用 LPTHW。我认为主要是因为它是一本实践学习的书籍,而不仅仅是理论性的。我正在使用这本书和 Codeacademy,因为我需要动手学习。所以,如果你对他的方法持批评态度,那么你会建议谁的方法呢? - sayth
2
@sayth:我同意他的一般方法,但它引起了很多问题。这些问题已经消失了,也许他已经解决了这些问题。例如,这个问题现在被列为本章的“常见问题”,Zed告诉你去阅读文档并自己解决。所以现在这可能是一本好书。 - Lennart Regebro
1
作为一名程序员,我在过去的15年中从未需要截断文件......如果您打开一个“读+写”文件,则可能需要这样做。如果您使用target.seek(0)而不是target.truncate(),则如果您写入文件的内容比原始内容短,文件末尾将会有“缓冲区下溢”样式的垃圾数据。我在我的答案https://dev59.com/AG455IYBdhLWcg3wD_2Z#31863528中解释了我的经验。 - Bruno Bronosky
在这种情况下,您不会以读写模式打开它,而是以写模式打开它。 - Lennart Regebro
如果文件是通过低级函数os.open打开的,则可能需要进行文件截断。或者,为了默认执行该操作,请不要忘记传递os.O_TRUNC标志。 - AndreyS Scherbakov

22

尽管在以'w'模式打开时截断不是很有用,但在'r+'模式下是有用的。虽然这不是OP的问题,但我会留下这个回答,以供像我一样通过谷歌搜索到这里的其他人参考。

假设你使用(以'r+'模式)打开了一个缩进了5行的json文件,并将 json.load 读入的对象修改为仅3行。如果在将数据写回文件之前使用 target.seek(0) ,则最终会得到2行垃圾数据。如果使用 target.truncate() 则不会出现这种情况。

我知道这似乎很明显,但我的BUG修复是因为一个对象多年来保持完全相同的大小...由于签名算法的更改而缩小了。(不明显的是我必须添加的单元测试,以防止类似问题再次发生。 我写了自己有史以来最长的文档字符串,解释为什么要使用两个荒谬的算法进行签名测试。)

希望这能帮助到某些人。


3
谢谢。尽管这个回答并没有确切回答问题,但它完全符合我的使用情况。 - BrDaHa

21
如果您在提问之前阅读了问题,他会为您回答问题:
附加学分:“如果您觉得自己不理解,请返回并使用注释技巧使其在脑海中变得清晰。每行上方的一个简单的英文注释将有助于您理解或至少让您知道需要更多研究的内容。
编写一个类似于上一练习的脚本,使用`read`和`argv`来读取刚刚创建的文件。
此文件中有太多的重复。使用字符串、格式和转义字符以仅使用一个`target.write()`命令打印出`line1`、`line2`和`line3`,而不是6个。
找出为什么我们必须传递一个“w”作为open的额外参数。提示:通过明确表示要写入文件来使open变得安全。
如果您使用“w”模式打开文件,那么是否真的需要`target.truncate()`?
去阅读Python的open函数文档,看看这是否属实。” -Zed Shaw。
他明确希望您自己找到这些事情,这就是他的附加学分的重要性。
他还明确表示,他希望您注意细节。每一个小细节都很重要。

13
这样做固然好,但并不总是有益的教学方式。我更希望从一开始就教授正确的方法,并解释为什么它是正确的,而不是从无用的内容开始,然后期望读者可能会关注看起来毫无意义的额外题目,而这个题目实际上隐藏着一条有用的信息。 - Platinum Azure
实际上,@PlatinumAzure,如果读者将要快速浏览而不阅读细节,他们最好养成包含无意义的nop调用.truncate()的习惯。这样他们就知道如何处理通过open(filename, 'r+')打开的可读写文件。 - Bruno Bronosky

8

使用truncate()函数可以根据当前文件的位置来声明要删除多少文件。不带参数时,truncate()函数的行为类似于w,而w总是会将整个文件清除。所以,这两种方法可以表现相同,但并非总是如此。


问题是为什么示例中有一个没有参数的 truncate() - Eric O. Lebigot

3

这只是标准posix语义的一种反映。请参见man fopen(3)。Python只是将其包装起来。


4
fopen(3) 函数说明已经提到它会自动截断,因此不需要使用额外的 truncate() 函数。 - ismail
正如Ignacio所指出的那样,这可能只是为了展示truncate()方法及其工作原理。 - Animesh
2
也许是这样,但如果已经被截断,你无法证明它是否有效。也许他应该先写点什么。 - Keith

1

最近遇到了一个场景,我需要创建大文件进行测试。其中一种快速的方法是使用 truncate 命令:

with open('filename.bin', 'wb') as f:
    f.truncate(1024 * 1024 * 1024)  # 1GB

该文件没有内容,但向操作系统报告您想要的大小,并在许多测试场景中起作用。


0
当您以写模式打开文件时,会截断原始文件(之前的所有内容都被删除)。然后您写入的任何内容都将添加到文件中。问题是,写操作想要从开头添加信息,并在指针留在末尾时引发 IOError 错误。对于这种类型的写入,您需要使用追加模式(使用 'a+' 参数打开文件)。

-1
场景:
我正在制作勒索软件并需要加密文件,我的目的不是完全加密文件,而只是加密一部分以使其损坏,因为我希望它在执行任务时更快,从而节省加密整个文件所需的时间,因此我决定仅编辑一些文本。
现在 如果我使用write,那么我的目的就被破坏了,因为我必须从头到尾写入文件。那我该怎么办呢?
好吧,在这里可以使用truncate

以下是我的代码,它只取文件中最后16位数字的标记:

with open('saver.txt', 'rb+') as f:
    text_len = len(f.read())
    f.truncate(text_len-16)
    f.close()

我打开文件
从文件中截取16个字符,稍后将由我替换。
请注意,我在只读模式下使用它,如果我在写入模式下使用,则文件将被完全截断,并且当我们的截断命令出现时会抛出错误。
回答这个问题已经过去8.4年了。:)


3
勒索软件仅用于教育目的。 - 1UC1F3R616

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