2011-12-05 31 views
2

我正在製作一個腳本,用於檢查/創建/更新項目中源文件頂部的版權聲明。將(生成的)標題添加到多個文件的最有效方法?

這通常是I/O限制,因爲每次使用腳本時,標題(缺失或其他)都會變得更大(例如,爲現有通知添加更多年),因此文件的其餘部分必須是重新定位到稍後的偏移量。這意味着讀取整個文件,然後寫回(+我想要的小標題更改)。

它發生在我身上,有可能是一個更有效的方法來做到這一點。這個用例並不罕見嗎?


我天真地以爲有可能尋求到一個負的,你可以尋求過去的文件(這通常會導致稀疏文件)結束以同樣的方式抵消。

import os 
fh = file("code.py", "rb+") 
original_size = os.fstat(fh.fileno()).st_size 
data = fh.read() 

# `prefix` should be prepended to the file 
# `updated_data` is anchored to offset 0, and likely only a 
# few 10s of bytes long (unlike the original file) 
# `suffix should` be postpended to the file 
prefix, updated_data, suffix = get_changes(data) 

fh.seek(0) 
fh.write(updated_data) 

# WISHFUL THINKING. Not possible to seek to a negative offset. 
fh.seek(-1 * len(prefix)) 
fh.write(prefix) 

fh.seek(max(original_size, len(updated_data))) 
fh.write(suffix) 

fh.close() 

環境的東西:

  • 的Python V2.6
  • 的GNU/Linux(Red Hat企業5 + Ubuntu的10.04,如果它事項)
+1

尋求替代回回,你爲什麼不能寫前綴第一,其次是updated_data其次後綴? – Abhijit

+0

沒理由,我只想說明我的數據是如何構建的。在生產代碼中,我會避​​免不必要的尋求,因爲你建議。 – RobM

回答

4

你可以尋求到負索引如果你通過從哪裏參數到file.seek,否則它被認爲是絕對的(因此不允許負向位置)。

import os 
f = open('insert.txt', 'r+') 
f.seek(3) 
f.seek(-1, os.SEEK_CUR) # will go back one position 
f.seek(-1, os.SEEK_END) # one position before the end of the file 

這不會真正幫助你,雖然 - 中間寫入字節將覆蓋現有的字節,而不是洗牌一切向前看。

您可以通過在文件開始時保留固定數量的頭部字節來達到目的 - 這就是二進制文件格式如何避免在更改時寫出整個文件。不過,我不會推薦它用於源文件。這很容易出錯 - 如果你弄錯了(或者你想寫的頭文件太長),那麼代碼的開始可能會被頭文件維護腳本覆蓋。

儘管如此,混合方法也許有效。

  • 第一次處理的文件,寫頭的緩慢方式(再次寫出整個文件),預留未來增長的一些額外的空間,並把一個哨兵在標題的末尾。哨兵應該是人類可讀的,並且不會無意中破壞。
  • 然後,下一次您需要編寫標題時,請閱讀標題(直到您知道需要的長度)。如果哨兵位於正確的位置,您可以使用快速覆蓋技術。
  • 如果不是,則需要再次以慢速方式寫入標題。

一些代碼(不處理頭大小變化):

import sys 
import os 

RESERVED = 40 
SENTINEL = '\n### HEADER ENDS ###\n' 

def pad(heading): 
    free_space = RESERVED - len(heading) 
    padding = ('#' * free_space) if free_space > 0 else '' 
    return heading + padding 

def _write_header_slow(fname, text): 
    # Do this in chunks instead if you have large files. 
    dest = fname + '.temp' 
    with open(fname) as infile: 
     content = infile.read() 
    with open(dest, 'w') as outfile: 
     outfile.write(text) 
     outfile.write(SENTINEL) 
     outfile.write(content) 
    os.rename(dest, fname) 

def write_header(fname, text): 
    if not text.endswith('\n'): 
     text += '\n' 
    assert len(text) < RESERVED, 'too much for the header!' 
    padded = pad(text) 
    with open(fname, 'rb+') as f: 
     current_header = f.read(RESERVED + len(SENTINEL)) 
     if current_header.endswith(SENTINEL): 
      f.seek(0) 
      print 'fast path!' 
      f.write(padded) 
     else: 
      print 'slow path):' 
      _write_header_slow(fname, text) 

if __name__ == '__main__': 
    write_header(sys.argv[1], sys.argv[2]) 
相關問題