4

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

4

5 回答 5

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-----------------------------------------------------']
>>> 
于 2014-06-04T18:47:58.500 回答
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

看这个作品

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


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

于 2014-06-04T19:24:36.597 回答
0

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

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变量将保存嵌套循环停止的标题行(don 'else不要在这里使用,否则这将不起作用)。

于 2014-06-04T18:59:37.983 回答
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
于 2014-06-04T19:52:18.360 回答
0

这是一种对您的原始代码几乎没有更改的方法。这取决于您的情况,但有时只做您想做的事情会更容易,而不必担心重新组织/重构其他一切!如果你想推回一些东西以便它再次迭代出来,那么就让它如此你就可以了!

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

因此,每当我们在其他地方读取需要重新处理的内容时,将其附加到双端队列对象并突破。

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...
于 2015-07-16T02:14:03.660 回答