2011-12-05 33 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]) 
相关问题