如何在不迭代的情况下读取行



我有一个文本文件,我设置了一个条件,我需要每隔一行提取一个文本块,但是文本块可以是任意数量的行(一个FASTA文件,对于任何生物信息学人员来说)。它基本上是这样设置的:

> header, info, info
TEXT-------------------------------------------------------
----------------------------------------------------
>header, info...
TEXT-----------------------------------------------------

…等等

我试图提取"TEXT"部分。下面是我设置的代码:

for line in ffile:
    if line.startswith('>'):
      # do stuff to header line
        try:
            sequence = ""
            seqcheck = ffile.next() # line after the header will always be the beginning of TEXT
            while not seqcheck.startswith('>'):
                        sequence += seqcheck
                        seqcheck = ffile.next()
        except:       # iteration error check
            break

这不起作用,因为每次调用next()时,它都会继续for循环,这导致我跳过很多行并丢失大量数据。我怎么能在不移动迭代器的情况下"偷看"下一行呢?

我想如果你能检查数据不是从'>'开始的,那就容易多了。

>>> content = '''> header, info, info
... TEXT-------------------------------------------------------
... ----------------------------------------------------
... >header, info...
... TEXT-----------------------------------------------------'''
>>> 
>>> f = StringIO(content)
>>> 
>>> my_data = []
>>> for line in f:
...   if not line.startswith('>'):
...     my_data.append(line)
... 
>>> ''.join(my_data)
'TEXT-------------------------------------------------------n----------------------------------------------------nTEXT-----------------------------------------------------'
>>> 

更新:

@tobias_k:

>>> def get_content(f):
...   my_data = []
...   for line in f:
...     if line.startswith('>'):
...       yield my_data
...       my_data = []
...     else:
...       my_data.append(line)
...   yield my_data  # the last on
... 
>>> 
>>> f.seek(0)
>>> for i in get_content(f):
...   print i
... 
[]
['TEXT-------------------------------------------------------n', '----------------------------------------------------n']
['TEXT-----------------------------------------------------']
>>> 

你考虑过正则表达式吗?:

txt='''
> header, info, info
TEXT----------------------------------------------------------------
TEXT2-------------------------------------------
>header, info...
TEXT-----------------------------------------------------'''

import re
for header, data in ((m.group(1), m.group(2)) for m in re.finditer(r'^(?:(>.*?$)(.*?)(?=^>|Z))', txt, re.S | re.M)):
    # process header
    # process data
    print header, data

查看此作品

这将给你你的头和数据从头在一个元组做你需要做的事。


如果文件很大,可以使用mmap来避免将整个文件读入内存。

这是另一种方法。与我上面的评论相反,这个确实使用嵌套循环来收集属于一个文本块的所有行(所以它的逻辑不是那么展开),但其方式略有不同:

for line in ffile:
    if not line.startswith('>'):
        sequence = line
        for line in ffile:
            if line.startswith('>'): break
            sequence += line
        print "<text>", sequence
    if line.startswith('>'):
        print "<header>", line

首先,它使用第二个for循环(使用与外部循环完全相同的ffile迭代器),因此不需要try/except。其次,没有丢失行,因为我们将当前的line输入到sequence中,并且因为我们首先执行了非标头情况:当到达第二个if检查时,line变量将保存嵌套循环停止的标头行(不要在这里使用else,否则这将不起作用)。

我的建议是使用列表和enumerate:

lines = ffile.readlines()
for i, line in enumerate(lines):
    if line.startswith('>'):
        sequence = ""
        for l in lines[i+1:]:
            if l.startswith('>'):
                break
            sequence += l

这是一个对原始代码进行了很少更改的方法。这取决于您的情况,但有时只做您想做的事情会更容易,而不必担心重新组织/重构其他所有事情!如果你想把某些东西推回去,让它再次迭代,那就这样做吧!

在这里,我们实例化了一个deque()对象,它保存了先前读取的行。然后包装ffile迭代器,该迭代器对对象进行简单检查,并在从ffile中获取新行之前清除其中的条目。

因此,当我们读取到需要在其他地方重新处理的内容时,将其附加到deque对象并退出。

import cStringIO,collections
original_ffile=cStringIO.StringIO('''
> header, info, info
TEXT----------------------------------------------------------------
TEXT2-------------------------------------------
>header, info...
TEXT-----------------------------------------------------''')
def peaker(_iter,_buffer):
    popleft=_buffer.popleft
    while True:
        while _buffer: yield popleft() # this implements FIFO-style
        yield next(_iter) # we don't have to catch StopIteration here!
buf=collections.deque()
push_back=buf.append
ffile=peaker(original_ffile,buf)
for line in ffile:
    if line.startswith('>'):
        print "found a header! %s"%line[:-1]
        # do stuff to header line
        sequence = ""
        for seqcheck in ffile:
            if seqcheck.startswith('>'):
                print "oops, we've gone too far, pushing back: %s"%seqcheck[:-1]
                push_back(seqcheck)
                break
            sequence += seqcheck
输出:

found a header! > header, info, info
oops, we've gone too far, pushing back: >header, info...
found a header! >header, info...

相关内容

  • 没有找到相关文章

最新更新