2012-01-03 70 views
3

我在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: 

什麼是實現這個目的的一個很好的方式?

+0

您在問cmake或Python是否可以確定文件是否應該被覆蓋__before__代碼已經在Python腳本中生成了? – jknupp 2012-01-03 21:32:30

+0

不,他問是否可以避免在這種情況下更改文件的修改日期,因爲如果修改日期更改,構建系統(CMake)將重建文件,從而使構建時間更長。 – kindall 2012-01-03 21:38:11

+0

啊,現在有道理。 – jknupp 2012-01-03 21:40:56

回答

4

代碼和Neil G想法,Petr 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通常會大於 行,這會導致讀取次數減少。
+0

+1,謝謝。爲什麼要複製而不移動? – 2012-01-05 18:11:16

+0

好主意!編輯... – unutbu 2012-01-05 18:29:05

+0

嗯......我不太清楚爲什麼,但是當我用/etc/test.dat以777權限測試這段代碼但被另一個用戶所擁有時,'shutil.copyfile'成功,但'shutil。移動「導致OSError。 – unutbu 2012-01-05 18:35:16

3

我建議你寫了這樣的自己的類文件對象:

  • __enter__:創建一個臨時文件
  • __exit__:比較與舊文件的臨時文件的內容(如果存在)如果他們是不一樣的,然後通過臨時文件

本文取代舊文件是相當有幫助的理解with聲明:Understanding Python's "with" statement

+0

不錯的解決方案。要用臨時文件替換舊文件,是否有可能以某種方式將舊文件指向臨時文件?而不是閱讀tempfile並將其寫入舊文件 – 2012-01-04 00:24:46

+0

@robertking yes:[shutil.move](http://docs.python.org/library/shutil.html#shutil.move) – gecco 2012-01-04 06:43:02

0

最簡單的方法是用Python做的cmake究竟是幹什麼的:有發電機檢查,如果輸入大於輸出更新,並僅生成,如果它是。

下面是我用類似的東西,一個片段:

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

沒有什麼說他正在使用本地文件系統上的文件作爲生成器的輸入。實際上,這可能是任何事情。 – jknupp 2012-01-03 21:51:36

+0

生成器是一個程序,所以在運行時很難知道哪些輸出文件會受到我對程序所做的更改的影響。 – 2012-01-03 21:59:24

+0

在這種情況下,要麼執行@ gecco的答案。另一種選擇是讓你的生成器寫入一個StringIO.StringIO。然後編寫一個名爲'write_if_different(stringio,filename)'的快速函數' – thesamet 2012-01-03 22:14:23

3

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

寫您的新文件到一個tmp目錄,比較對你的工作目錄 ,並調過來改動過的文件。然後刪除tmp。

+0

啊,謝謝!就在我編寫完gecco的解決方案後...... – 2012-01-03 22:42:11

+0

'filecmp'是否可以與'NamedTemporaryFile'一起使用? – 2012-01-03 22:45:03

+0

取決於。它在unix系統上效果最好(相對於win)。嘗試一下。 – joel3000 2012-01-03 23:00:26