Самый эффективный способ добавить (сгенерированный) заголовок ко многим файлам? - PullRequest
2 голосов
/ 05 декабря 2011

Я работаю над сценарием, который проверяет / создает / обновляет уведомления об авторских правах в верхней части исходных файлов в моем проекте.

Обычно это связано с вводом / выводом, поскольку заголовок (отсутствует или нет) имеет тенденциючтобы увеличиваться при каждом использовании сценария (например, добавление большего количества лет к существующему уведомлению), и поэтому остальная часть файла должна быть перемещена в более позднее смещение.Это означает чтение всего файла, а затем запись его обратно (+ небольшие изменения заголовка, которые я хочу).

Мне приходит в голову, что, вероятно, существует более эффективный способ сделать это.Этот сценарий использования не так уж необычен, не так ли?


Я наивно представлял, что можно искать отрицательное смещение таким же образом, как вы можете искать за концом файла (чтообычно приводит к разреженным файлам).

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 Enterprise 5 + Ubuntu 10.04, если это имеет значение)

1 Ответ

4 голосов
/ 05 декабря 2011

Вы можете искать отрицательный индекс, если передаете аргумент fromce в 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])
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...