Как ограничить размер файла при его написании?

Я использую выходные потоки из модуля ввода-вывода и записываю в файлы. Я хочу иметь возможность определить, когда я написал 1G данных в файл, а затем начать запись во второй файл. Я не могу понять, как определить, сколько данных я написал в файл.

есть ли что-то простое, встроенное в io? Или мне нужно подсчитать байты перед каждой записью вручную?

6 ответов


см. документацию Python для Объекты, в частности рассказать().

пример:

>>> f=open('test.txt','w')
>>> f.write(10*'a')
>>> f.tell()
10L
>>> f.write(100*'a')
>>> f.tell()
110L

Если вы используете этот файл для ведения журнала, я предлагаю использовать RotatingFileHandler в модуле регистрации, как это:

import logging
import logging.handlers

file_name = 'test.log'

test_logger = logging.getLogger('Test')
handler = logging.handlers.RotatingFileHandler(file_name, maxBytes=10**9)
test_logger.addHandler(handler)

N. B: вы также можете использовать этот метод, даже если вы не используете его для ведения журнала, Если вам нравится делать хаки:)


см. метод tell () для объекта stream.


один довольно прямой подход заключается в подклассе builtinfileclass и иметь его отслеживать количество выходных данных, которые записываются в файл. Ниже приведен пример кода, показывающий, как это можно сделать, что, по-видимому, в основном работает.

Я говорю в основном потому, что размер производимых файлов иногда немного превышает максимальный при его тестировании, но это потому, что тестовый файл был открыт в режиме "текст" и в Windows это означает, что все'\n' перевод строки символы преобразуются в'\r\n'(carriage-return, linefeed) пары, который выбрасывает аккумулятор размера. Кроме того, в настоящее время, вbufsizeаргумент, что стандартfile()иopen() функции accept не поддерживаются, поэтому размер и режим системы по умолчанию всегда будут использоваться.

в зависимости от того, что именно вы делаете, проблема размера может не быть большой проблемой - однако для больших максимальных размеров это может быть значительно. Если у кого-то есть хорошее независимое от платформы исправление для этого, безусловно, дайте нам знать.

import os.path
verbose = False

class LtdSizeFile(file):
    ''' A file subclass which  limits size of file written to approximately "maxsize" bytes '''
    def __init__(self, filename, mode='wt', maxsize=None):
        self.root, self.ext = os.path.splitext(filename)
        self.num = 1
        self.size = 0
        if maxsize is not None and maxsize < 1:
            raise ValueError('"maxsize: argument should be a positive number')
        self.maxsize = maxsize
        file.__init__(self, self._getfilename(), mode)
        if verbose: print 'file "%s" opened' % self._getfilename()

    def close(self):
        file.close(self)
        self.size = 0
        if verbose: print 'file "%s" closed' % self._getfilename()

    def write(self, text):
        lentext =len(text)
        if self.maxsize is None or self.size+lentext <= self.maxsize:
            file.write(self, text)
            self.size += lentext
        else:
            self.close()
            self.num += 1
            file.__init__(self, self._getfilename(), self.mode)
            if verbose: print 'file "%s" opened' % self._getfilename()
            self.num += 1
            file.write(self, text)
            self.size += lentext

    def writelines(self, lines):
        for line in lines:
            self.write(line)

    def _getfilename(self):
        return '{0}{1}{2}'.format(self.root, self.num if self.num > 1 else '', self.ext)

if __name__=='__main__':
    import random
    import string

    def randomword():
        letters = []
        for i in range(random.randrange(2,7)):
            letters.append(random.choice(string.lowercase))
        return ''.join(letters)

    def randomsentence():
        words = []
        for i in range(random.randrange(2,10)):
            words.append(randomword())
        words[0] = words[0].capitalize()
        words[-1] = ''.join([words[-1], '.\n'])
        return ' '.join(words)

    lsfile = LtdSizeFile('LtdSizeTest.txt', 'wt', 100)
    for i in range(100):
        sentence = randomsentence()
        if verbose: print '  writing: {!r}'.format(sentence)
        lsfile.write(sentence)

    lsfile.close()

Я заметил двусмысленность в вашем вопросе. Вы хотите, чтобы файл был (a) над (b) под (c) ровно 1GiB большим, перед переключением?

легко сказать, если вы перешли. tell() достаточно для такого рода вещей; просто проверьте if tell() > 1024*1024*1024: и вы узнаете.

проверка, если вы находитесь под 1GiB, но перейдете на 1GiB при следующей записи, является аналогичной техникой. if len(data_to_write) + tell > 1024*1024*1024: хватит.

самое сложное, что нужно сделать, это получить файл точно 1GiB. Вы нужно будет tell() длина файла, а затем разделите ваши данные соответствующим образом, чтобы точно поразить метку.

независимо от того, какую именно семантику вы хотите,tell() всегда будет по крайней мере так же медленно, как делать подсчет самостоятельно, и, возможно, медленнее. Это не означает, что это неправильно; если вы пишете файл из потока, то вы почти наверняка захотите tell() вместо того, чтобы надеяться, что вы правильно опередили другие потоки писать в тот же файл. (И сделайте ваши замки, etc. но это уже другой вопрос.)

~ С.


рекомендую подсчет. Я не знаю никакого внутреннего языкового счетчика. Кто-то еще упомянул использование tell(), но внутренний счетчик займет примерно столько же работы и устранит постоянные вызовы ОС.

#pseudocode
if (written + sizeOfNew > 1G) {
    rotateFile()
}