我正在编写一个脚本,检查/创建/更新项目源文件顶部的版权声明。
这通常是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()
环境材料:
答案 0 :(得分:4)
如果将 whence 参数传递给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])