Lectura en archivo bloque por bloque usando un delimitador especificado en python

Tengo un archivo input_file.fa como este (formato FASTA ):

> header1 description data data data >header2 description more data data data 

Quiero leer en el archivo un fragmento a la vez, de modo que cada fragmento contenga un encabezado y los datos correspondientes, por ejemplo, el bloque 1:

 > header1 description data data data 

Por supuesto que podría leer el archivo así y dividirlo:

 with open("1.fa") as f: for block in f.read().split(">"): pass 

Pero quiero evitar leer todo el archivo en la memoria , porque los archivos suelen ser grandes.

Puedo leer en el archivo línea por línea, por supuesto:

 with open("input_file.fa") as f: for line in f: pass 

Pero idealmente lo que quiero es algo como esto:

 with open("input_file.fa", newline=">") as f: for block in f: pass 

Pero me sale un error:

ValueError: valor de nueva línea ilegal:>

También he intentado usar el módulo csv , pero sin éxito.

Encontré esta publicación de hace 3 años, que proporciona una solución basada en un generador para este problema, pero no parece tan compacta, ¿es esta realmente la única / mejor solución? Sería bueno si es posible crear el generador con una sola línea en lugar de una función separada, algo como este pseudocódigo:

 with open("input_file.fa") as f: blocks = magic_generator_split_by_> for block in blocks: pass 

Si esto es imposible, entonces creo que podrías considerar mi pregunta como un duplicado de la otra publicación, pero si es así, espero que la gente pueda explicarme por qué la otra solución es la única. Muchas gracias.

Una solución general aquí será escribir una función de generador para esto que produzca un grupo a la vez. En esta memoria, solo almacenará un grupo a la vez.

 def get_groups(seq, group_by): data = [] for line in seq: # Here the `startswith()` logic can be replaced with other # condition(s) depending on the requirement. if line.startswith(group_by): if data: yield data data = [] data.append(line) if data: yield data with open('input.txt') as f: for i, group in enumerate(get_groups(f, ">"), start=1): print ("Group #{}".format(i)) print ("".join(group)) 

Salida:

 Group #1 > header1 description data data data Group #2 >header2 description more data data data 

Para los formatos FASTA en general, recomendaría usar el paquete Biopython .

Un enfoque que me gusta es usar itertools.groupby junto con una simple función key :

 from itertools import groupby def make_grouper(): counter = 0 def key(line): nonlocal counter if line.startswith('>'): counter += 1 return counter return key 

Úsalo como:

 with open('filename') as f: for k, group in groupby(f, key=make_grouper()): fasta_section = ''.join(group) # or list(group) 

Solo necesita la join si tiene que manejar el contenido de una sección completa como una sola cadena. Si solo está interesado en leer las líneas una por una, simplemente puede hacer:

 with open('filename') as f: for k, group in groupby(f, key=make_grouper()): # parse >header description header, description = next(group)[1:].split(maxsplit=1) for line in group: # handle the contents of the section line by line 
 def read_blocks(file): block = '' for line in file: if line.startswith('>') and len(block)>0: yield block block = '' block += line yield block with open('input_file.fa') as f: for block in read_blocks(f): print(block) 

Esto se leerá en el archivo línea por línea y obtendrá los bloques con la statement de rendimiento. Esto es perezoso para que no tenga que preocuparse por la gran huella de memoria.