Python 将(生成的)头添加到多个文件的最有效方法?

Python 将(生成的)头添加到多个文件的最有效方法?,python,file-io,python-2.6,Python,File Io,Python 2.6,我正在编写一个脚本,用于检查/创建/更新项目中源文件顶部的版权声明 这通常是I/O绑定的,因为每次使用脚本时,头(缺失或其他)往往会变大(例如,向现有通知添加更多年),因此必须将文件的其余部分重新定位到稍后的偏移量。这意味着读取整个文件,然后将其写回(+我想要的小标题更改) 我突然想到,也许有一种更有效的方法可以做到这一点。这个用例并不少见,是吗 我天真地认为,可以像查找文件末尾(通常会导致稀疏文件)一样查找负偏移量 环保材料: Python v2.6 GNU/Linux(红帽企业版5+Ub

我正在编写一个脚本,用于检查/创建/更新项目中源文件顶部的版权声明

这通常是I/O绑定的,因为每次使用脚本时,头(缺失或其他)往往会变大(例如,向现有通知添加更多年),因此必须将文件的其余部分重新定位到稍后的偏移量。这意味着读取整个文件,然后将其写回(+我想要的小标题更改)

我突然想到,也许有一种更有效的方法可以做到这一点。这个用例并不少见,是吗


我天真地认为,可以像查找文件末尾(通常会导致稀疏文件)一样查找负偏移量

环保材料:

  • Python v2.6
  • GNU/Linux(红帽企业版5+Ubuntu 10.04,如果需要的话)

如果将whence参数传递给,则可以寻求负索引,否则它被假定为绝对索引(因此不允许负位置)

这对你没有帮助,但是在中间写字节会覆盖现有的字节,而不是把所有的东西向前拖动。p> 您可以通过在文件的开头保留固定数量的头字节来实现所需的功能—这就是二进制文件格式在更改时避免写入整个文件的方式。不过,我不建议将其用于源文件。这将是非常容易出错的-如果你搞错了(或者你想写的头太长了),那么你的代码的开头可能会被头维护脚本覆盖

不过,一种混合方法可能会奏效

  • 第一次处理文件时,以缓慢的方式写入头文件(再次写出整个文件),为将来的增长保留一些额外的空间,并在头文件的末尾放置一个哨兵。哨兵应该是人类可读的,并且不容易在无意中被打破
  • 然后,下次你需要写标题的时候,读入标题(直到你知道你需要的长度)。如果哨兵在正确的位置,您可以使用快速覆盖技术
  • 如果没有,您需要再次以慢速方式写入标题
某些代码(不处理标头大小的更改):

导入系统 导入操作系统 保留=40 SENTINEL='\n########\n' def pad(标题): 自由空间=保留-长度(标题) 如果可用空间>0,则填充=(“#”*可用空间),否则为“” 返回标题+填充 定义-写入-标题-速度慢(fname,text): #如果您有大文件,请将其分块执行。 dest=fname+'.temp' 将open(fname)作为内嵌: content=infle.read() 打开(dest,'w')作为输出文件: outfile.write(文本) outfile.write(哨兵) outfile.write(内容) 操作系统重命名(dest,fname) def write_标题(fname,text): 如果不是text.endswith('\n'): text+='\n' 断言len(text)与其来回奔波,不如先写前缀,再写更新后的数据,再写后缀?没有理由,我只是想说明我的数据是如何构造的。在生产代码中,我会像你建议的那样避免不必要的搜索。
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()
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])