2010-10-22 211 views
10

我正在使用io模塊的輸出流並寫入文件。我希望能夠檢測何時將1G數據寫入文件,然後開始寫入第二個文件。我似乎無法弄清楚如何確定我寫入文件的數據量。如何在使用python寫入文件時限制文件大小

有沒有簡單的內置到io?或者我可能必須在每次手動寫入之前計算字節數?

回答

9

參見Python文檔爲File Objects,具體tell()。

例子:

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

請參閱流對象上的tell()方法。

0

我推薦計數。沒有我知道的內部語言計數器。其他人提到使用tell(),但內部計數器將花費大致相同的工作量並消除常量OS調用。

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

只是如果你是明智的使用'告訴()',並允許一些誤差,這比開銷計數少了很多。 – 2010-10-22 17:08:27

+3

我無法想象如何可以少開銷。添加到整數並將其與最大值進行比較都是單指令操作,如果類型有點大,則可以接近它。 tell()正在沿着系統調用樹走。此外,無論何時告訴(),您都在檢查已寫入的內容。你可以在沒有太多麻煩的情況下編寫代碼,但是... – 2010-10-25 13:56:39

14

如果你正在使用這個文件我建議使用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) 

注意:您還甚至可以使用這個方法如果你不使用它進行日誌記錄,如果你喜歡做黑客:)

+1

有關logrotate的信息有助於理解這種方法:http://www.debian-administration.org/articles/117 – 2012-07-13 20:44:50

+1

需要添加:導入日誌記錄。 handlers' – hwang 2013-05-23 21:39:37

1

一個相當直接的方法是子類的內置file類並讓它跟蹤寫入文件的輸出量。下面是一些示例代碼,展示了可能如何完成似乎主要工作。

我說的主要是因爲生成的文件的大小有時會略微超過最大值,但是這是因爲測試文件是在「文本」模式下打開的,而在Windows上,這意味着所有'\n'換行符都會得到轉換爲'\r\n'(回車,換行)對,這將拋出大小累加器。另外,正如目前所寫,標準file()open()函數接受的參數bufsize不受支持,因此係統的默認大小和模式將始終使用。

根據你正在做的事情,尺寸問題可能不是什麼大問題 - 但是對於大尺寸的最大尺寸,它可能會明顯偏離。如果任何人有一個良好的平臺獨立的解決方案,通過一切手段讓我們知道。

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

我注意到你的問題有一個模棱兩可的問題。 (a)在切換之前,(b)在(c)正好大於1GiB的情況下,你想要文件成爲(a)

很容易判斷你是否已經過去了。 tell()就足夠了那種事情;只需檢查if tell() > 1024*1024*1024:,你就會知道。

檢查您是否在1GiB以下,但在您下一次寫入時將超過1GiB,是一種類似的技術。 if len(data_to_write) + tell > 1024*1024*1024:就足夠了。

最棘手的事情就是讓文件準確地達到1GiB。您將需要tell()文件的長度,然後適當地分割您的數據以便精確地打上標記。

無論您想要哪種語義,tell()總是會至少和自己計算一樣慢,並且可能會更慢。這並不意味着這是一件錯誤的事情;如果你是從一個線程寫入文件,那麼你幾乎肯定會想要tell(),而不是希望你已經正確地搶佔了其他線程寫入同一個文件。 (和你的鎖等,但這是另一個問題。)

順便說一句,我注意到你最後幾個問題的明確方向。您是否瞭解Freenode(irc.freenode.net)上的#twisted和#python IRC頻道?你會得到更及時,更有用的答案。

〜C.