2011-11-13 26 views
2

我有一個文件,它是需要約100MB,看起來像這樣:如何閱讀可變多行數據的文件在Python

#meta data 1  
skadjflaskdjfasljdfalskdjfl 
sdkfjhasdlkgjhsdlkjghlaskdj 
asdhfk 
#meta data 2 
jflaksdjflaksjdflkjasdlfjas 
ldaksjflkdsajlkdfj 
#meta data 3 
alsdkjflasdjkfglalaskdjf 

此文件包含對應於幾個可變元數據的一行只包含字母數字字符的長度數據。什麼是閱讀此數據到一個簡單的列表這樣的最佳方式:

data = [[#meta data 1, skadjflaskdjfasljdfalskdjflsdkfjhasdlkgjhsdlkjghlaskdjasdhfk], 
     [#meta data 2, jflaksdjflaksjdflkjasdlfjasldaksjflkdsajlkdfj], 
     [#meta data 3, alsdkjflasdjkfglalaskdjf]] 

我最初的想法是使用read()方法讀取整個文件到內存中,然後使用正則表達式解析數據進所需的格式。有更好的pythonic方式嗎?所有元數據行都以一個八進制開頭,所有數據行都是字母數字。謝謝!

回答

4

itertools.groupby提供了一種簡單的方式來收集線成組:

import itertools 

data=[] 
with open('data.txt','r') as f: 
    for key,group in itertools.groupby(f,lambda line: line.startswith('#meta')): 
     if key: 
      meta=next(group).strip() 
     else: 
      lines=''.join(group).strip() 
      data.append((meta,lines)) 
print(data)    

產生

[('#meta data 1', 'skadjflaskdjfasljdfalskdjfl\nsdkfjhasdlkgjhsdlkjghlaskdj\nasdhfk'), ('#meta data 2', 'jflaksdjflaksjdflkjasdlfjas\nldaksjflkdsajlkdfj'), ('#meta data 3', 'alsdkjflasdjkfglalaskdjf')] 

表達

itertools.groupby(f,lambda line: line.startswith('#meta')) 

返回迭代器。它循環遍歷f中的行,並在每行上調用lambda函數。當它遇到以#meta開頭的行時,該函數返回True,否則返回False

itertools.groupby收集所有返回相同值的連續行。

因此,以#meta開頭的行將被放置在其自己的組中,然後所有後續行不以#meta開頭的行被放置在下一個組中,依此類推。

keylambda函數的返回值。在這種情況下,它將是TrueFalse

+0

哇,這是很棒的!我遇到的唯一困難是我的輸出給了我'[(假,'skadjflaskdjfasljdfalskdjfl \ nsdkfjhasdlkgjhsdlkjghlaskdj \ nasdhfk')...'我似乎無法明白爲什麼我會得到一個布爾值以及它爲什麼是假的? – drbunsen

+0

看起來你可能正在印刷'key'而不是'meta'?你使用'data.append((key,lines))'?如果是這樣,請更改'key' - >'meta'。 – unutbu

+0

是的,這是問題所在。 – drbunsen

0

我想類似的東西:

result = [] 
for line in file.readlines(): 
    if line[0] == '#': 
     result.append([line]) 
    else: 
     if len(result[-1]) == 1: 
      result[-1].append(line) 
     else: 
      result[-1][-1] += line 

未經測試。

0

我會保持它的簡單,類似:

data = [] # result 
lastmeta = None # the last metadata line seen 
chunks = [] # lines since the last metadata line 
for line in input: 
    if line[0] == '#': # metadata 
     if lastmeta: # need to flush data we've collected 
      data.append((lastmeta, ''.join(chunks)) 
     lastmeta = line 
    else: 
     chunks.append(line) 
1

我不知道這是否將是最快的方式,但是從我的頭頂:

data = [] 
with open('input.file', 'r') as fp: 
    for line in fp: 
     line = line.strip() 
     if line[0] == '#': 
      data.append((line, [])) 
     else: 
      data[-1][1].append(line) 
data = [(X, ''.join(Y)) for X, Y in data] 
+0

謝謝,這是一個很酷的答案。我從來沒有想過這樣做。 – drbunsen