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

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

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

Ответ 1

См. документацию Python для Файловые объекты, в частности tell().

Пример:

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

Ответ 2

Если вы используете этот файл для целей ведения журнала, я предлагаю использовать 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: вы также можете использовать этот метод, даже если вы не используете его для ведения журнала, если вам нравится делать хаки:)

Ответ 3

См. метод tell() для объекта потока.

Ответ 4

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

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

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

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()

Ответ 5

Я заметил двусмысленность в вашем вопросе. Вы хотите, чтобы файл был (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() вместо того, чтобы надеяться, что вы правильно вытеснили другие потоки, пишущие в один и тот же файл. (И делай свои замки и т.д., Но это другой вопрос.)

Кстати, я заметил определенное направление в ваших последних парах вопросов. Вы знаете о #twisted и #python IRC-каналах на Freenode (irc.freenode.net)? Вы получите более своевременные, более полезные ответы.

~ C.

Ответ 6

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

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