【问题标题】:Easy way of not overwriting file when output is the same输出相同时不覆盖文件的简单方法
【发布时间】:2012-01-03 21:29:54
【问题描述】:

我有一个 Python 中的 C++ 代码生成器,可以生成许多源文件。大多数时候,只有一个文件发生了变化,但是由于生成器重新生成了所有文件,所以它们都被重新构建了。有没有办法让 Python 不覆盖文件,或者让 cmak 使用校验和来查看需要重建的内容,而不仅仅是使用文件日期?

我在想这样的事情在 Python 中会很容易:如果我可以替换

with open('blah', 'w') as f:

用这个:

with open_but_only_overwrite_if_total_output_is_different('blah', 'w') as f:

有什么好的方法可以做到这一点?

【问题讨论】:

  • 您是在询问 cmake 或 Python 是否可以确定是否应该在 Python 脚本中生成代码之前覆盖文件?
  • 不,他问的是在这种情况下是否可以避免更改文件的修改日期,因为如果修改日期发生变化,构建系统(CMake)将重建文件,从而使构建时间更长.

标签: python cmake


【解决方案1】:

结合Neil GPetr Viktoringeccojoel3000的代码和思路:

import contextlib
@contextlib.contextmanager
def write_on_change(filename):
    with tempfile.NamedTemporaryFile(delete=False) as f:
        yield f
        tempname = f.name
    try:
        overwrite = not filecmp.cmp(tempname,filename)
    except (OSError,IOError):
        overwrite = True
    if overwrite:
        shutil.copyfile(tempname,filename)
    os.unlink(tempname)

一些小补充(希望有所改进):

  • shutil.copyfile 只将tempname 的内容复制到 filename,同时保留文件权限和文件等元数据 所有权。
  • filecmp.cmp 检查文件的大小 如果大小不匹配,则返回 False。这可能是一个不错的 如果文件很大并且文件附加了一些东西,则加速 结尾。它还一次读取和比较bufsize = 8*1024 字节, 而不是一次一行。 bufsize 通常会大于 行,这将导致更少的读取。

【讨论】:

  • 嗯...我不太清楚为什么,但是当我在 /tmp/test.dat 上以 777 权限测试此代码但由另一个用户拥有时,shutil.copyfile 成功,但 @987654334 @ 导致 OSError。
  • shutil.move 调用 shutil.copystat 尝试更改 atime 和 mtime。当您不拥有该文件时,这是不允许的。因此,对os.utime 的调用失败。 shutil.copyfile 不会更改元数据,因此可以正常工作。
  • yieldcontextlib.contextmanager 的出色用法!太好了!
【解决方案2】:

我建议你像这样编写我们自己的类文件对象:

  • __enter__:创建一个临时文件
  • __exit__: 比较临时文件和旧文件的内容(如果存在的话)如果不一样,就用临时文件替换旧文件

这篇文章对理解with语句很有帮助:Understanding Python's "with" statement

【讨论】:

  • 不错的解决方案。要用临时文件替换旧文件,是否可以以某种方式将旧文件指向临时文件?而不是读取临时文件并将其写入旧文件
  • @robertking 是:shutil.move
【解决方案3】:

使用 filecmp - http://docs.python.org/library/filecmp.html

将新文件写入 tmp 目录,与工作目录进行比较 , 并传输更改的文件。然后删除tmp。

【讨论】:

  • 啊,谢谢!就在我完成 gecco 解决方案的编码时……
  • 视情况而定。它在unix系统上效果最好(而不是win)。试试看。
  • 如果filecmp 接受文件句柄而不是文件名,这将是最好的解决方案。但是,+1,感谢您提请我注意。
  • 嗯。 NamedTemporaryFile 将为您提供一个可以使用的名称。我认为这应该与filecmp一起使用。我很好奇。有时间我会试试的。
  • 是的,你是对的。关闭文件后,我必须将 delete 设置为 False 并将临时文件名传递给 filecmp。这不是一个糟糕的解决方案,但不必关闭并重新打开文件会很好。
【解决方案4】:

最简单的方法是在 Python 中完全按照 cmake 的方式进行操作:让生成器检查输入是否比输出新,如果是则仅生成。

这是我用来做类似事情的 sn-p:

if (os.path.exists(output) and
    os.path.getmtime(source) <= os.path.getmtime(output)):
    print "Generated output %s is up-to-date." % output
    return

【讨论】:

  • 没有任何迹象表明他正在使用本地文件系统上的文件作为生成器的输入。实际上,它可以是任何东西。
  • 生成器是一个程序,因此在运行时很难知道哪些输出文件会受到我对程序所做的更改的影响。
  • 在这种情况下,要么按照@gecco 的答案实现一些东西。另一种选择是让您的生成器写入 StringIO.StringIO。然后编写一个名为write_if_different(stringio, filename)的快速函数
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2014-12-09
  • 2016-11-19
  • 2014-06-07
  • 1970-01-01
  • 1970-01-01
  • 2016-07-23
  • 1970-01-01
相关资源
最近更新 更多