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

时间:2014-06-04 18:37:06

标签: python file iteration file-manipulation

我有一个文本文件,我有一个条件设置,我需要每隔一行提取一大块文本,但文本块可以是任意数量的行(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循环,这会导致我跳过很多行并丢失大量数据。如何在没有向前移动迭代器的情况下“窥视”下一行?

5 个答案:

答案 0 :(得分:3)

我想如果你检查数据不是以'>'开头会更容易。

>>> 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-----------------------------------------------------']
>>> 

答案 1 :(得分:1)

您是否考虑过正则表达式?:

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

See this work

这将为您提供元组中该标题的标题和数据,以执行您需要执行的操作。


如果您的文件很大,请you can use mmap以避免将整个文件读入内存。

答案 2 :(得分:0)

这是另一种方法。与我上面的注释相反,这个 使用嵌套循环来收集属于一个文本块的所有行(所以这个逻辑不是那么分散),但是这样做的方式略有不同: / p>

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,否则这将无效。)

答案 3 :(得分:0)

我对偷看的建议是使用列表和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

答案 4 :(得分:0)

这是一种对原始代码进行很少更改的方法。这取决于你的情况,但有时你做你想做的事情更容易,而不必担心重新组织/重构其他一切!如果你想推送一些东西,那么它会再次被迭代,然后就这样做就可以了!

这里我们实例化一个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...