2010-11-11 66 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