2010-11-11 29 views
1

我的Python代碼支持以其他人創建的文件格式讀寫數據,文件格式爲BLT format。 BLT格式是白色空間和獨立的換行符,因爲新行被視爲與其他空白空間一樣。此格式的主要入口是一個「投票」,它以「0」結尾,例如,閱讀格式與換行無關的非常大的文件

1 2 3 0 

由於格式是獨立於換行,也可以寫成

1 2 
3 0 

或者你可以在一條線上有多個選票:

1 2 3 0 4 5 6 0 

這些文件可能非常大,因此我不想將整個文件讀入內存。由於數據不是基於行的,因此基於行的讀取很複雜。以有效利用內存的方式處理這些文件的好方法是什麼?

+0

你對BLT格式或擴展(Open STV)格式感興趣嗎?是你給每種格式的唯一規範的鏈接? – 2010-11-11 20:01:14

+0

完全披露,我寫了鏈接維基頁面並設計了擴展格式。我對原始格式非常感興趣,因爲它是獨立於新行的格式(由於我鼓勵人們使用換行符,因此未在wiki頁面中進行說明)。 – 2010-11-11 20:30:17

回答

3

對我來說,解決這個問題最直接的方法就是使用發電機。

def tokens(filename): 
    with open(filename) as infile: 
     for line in infile: 
      for item in line.split(): 
       yield int(item) 

def ballots(tokens): 
    ballot = [] 
    for t in tokens: 
     if t: 
      ballot.append(t) 
     else: 
      yield ballot 
      ballot = [] 

t = tokens("datafile.txt") 

for b in ballots(t): 
    print b 

我看@katrielalex發佈了一個發電機使用解決方案,當我發佈我的。我們之間的區別在於,我使用了兩個獨立的生成器,一個用於文件中的單個令牌,另一個用於您希望解析的特定數據結構。前者作爲參數傳遞給後者,其基本思想是可以爲要分析的每個數據結構編寫一個函數,如ballots()。您可以對發生器產生的所有內容進行迭代,或者在任一發生器上調用next()以獲取下一個標記或選票(當您用完時爲StopIteration例外做準備,或者寫入發生器以生成標記值,例如None他們用完了真實的數據,並檢查它)。

將整個事物包裝在一個類中是非常簡單的。其實...

class Parser(object): 

    def __init__(self, filename): 

     def tokens(filename): 
      with open(filename) as infile: 
       for line in infile: 
        for item in line.split(): 
         yield int(item) 

     self.tokens = tokens(filename) 

    def ballots(self): 
     ballot = [] 
     for t in self.tokens: 
      if t: 
       ballot.append(t) 
      else: 
       yield ballot 
       ballot = [] 

p = Parser("datafile.txt") 

for b in p.ballots(): 
    print b 
+0

這個解決方案(兩個發電機)比單發電機的回答更可取,但不夠遠。文件格式除了「選票」結構之外還有其他結構。因此,您需要一個生成器將文件解析爲令牌(**,其中可以包含引號**中的文本),以及每個不同結構的識別器(可能是生成器)。 – 2010-11-11 19:58:40

+0

是的,我沒有真正看完整的文件格式。 – kindall 2010-11-11 20:03:11

+0

感謝您的回答!缺乏現成的方法來抓取文件中的接下來的n個項目,與R的'scan'或者Fortran的'read'的緩解相比,真的會傷害Python中的數據管理。這種實現非常聰明和高效 - 但對語言新手來說並不明顯。 – Sharpie 2010-12-23 00:42:37

1

使用generator

>>> def ballots(f): 
...  ballots = [] 
...  for line in f: 
...    for token in line.split(): 
...      if token == '0': 
...        yield ballots 
...        ballots = [] 
...      else: 
...        ballots.append(token) 

這將逐行讀取文件中的行,分裂的所有空白,並通過一個附加線中的某個令牌到列表中。每當達到零時,該選票就是yield ed,並且列表重置爲空。

+0

我喜歡這個想法,但似乎迭代每個字符會很慢......我想知道我能否獲得內存效率和速度。 – 2010-11-11 19:26:33

+0

你可能會在嘗試之前抱怨它的速度有多慢。 :-)它一次讀取一行,所以它不像你有讀取每個項目的I/O調用。 – kindall 2010-11-11 19:30:47

+0

它**不會**遍歷每個字符; 'char'對於可以包含'「-2」或「10」(根據給定的鏈接)或(可能)'str(any_old_integer)' – 2010-11-11 19:35:12