我有一个文本文件,我设置了一个条件,我需要每隔一行提取一个文本块,但是文本块可以是任意数量的行(一个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...