Insertar línea en la posición especificada de un archivo de texto

Tengo un archivo de texto que se ve así:

blah blah foo1 bar1 foo1 bar2 foo1 bar3 foo2 bar4 foo2 bar5 blah blah 

Ahora quiero insertar 'foo bar' entre 'foo1 bar3' y 'foo2 bar4' .

Así es como lo hice:

 import shutil txt = '1.txt' tmptxt = '1.txt.tmp' with open(tmptxt, 'w') as outfile: with open(txt, 'r') as infile: flag = 0 for line in infile: if not line.startswith('foo1') and flag == 0: outfile.write(line) continue if line.startswith('foo1') and flag == 0: flag = 1 outfile.write(line) continue if line.startswith('foo1') and flag == 1: outfile.write(line) continue if not line.startswith('foo1') and flag == 1: outfile.write('foo bar\n') outfile.write(line) flag = 2 continue if not line.startswith('foo1') and flag == 2: outfile.write(line) continue shutil.move(tmptxt, txt) 

Esto funciona para mí, pero se ve bastante feo.

La mejor manera de realizar cambios “pseudo-in situ” en un archivo en Python es con el módulo de entrada de fileinput de la biblioteca estándar:

 import fileinput processing_foo1s = False for line in fileinput.input('1.txt', inplace=1): if line.startswith('foo1'): processing_foo1s = True else: if processing_foo1s: print 'foo bar' processing_foo1s = False print line, 

También puede especificar una extensión de copia de seguridad si desea mantener la versión anterior, pero esto funciona en la misma línea que su código: utiliza .bak como la extensión de copia de seguridad, pero también la elimina una vez que el cambio se ha completado con éxito.

Además de usar el módulo de biblioteca estándar correcto, este código usa una lógica más simple: para insertar una línea de "foo bar" después de cada serie de líneas que comienzan con foo1 , todo lo que necesita es un booleano (¿estoy dentro de esa carrera o no?) Y el bool en cuestión se puede establecer incondicionalmente solo en función de si la línea actual comienza de esa manera o no. Si la lógica precisa que desea es ligeramente diferente de esta (que es lo que deduje de su código), no debería ser difícil modificar este código en consecuencia.

Adaptando el ejemplo de Alex Martelli:

 import fileinput for line in fileinput.input('1.txt', inplace=1): print line, if line.startswith('foo1 bar3'): print 'foo bar' 

Recuerde que un iterador es un objeto de primera clase. Se puede utilizar en múltiples para las declaraciones.

Aquí hay una manera de manejar esto sin un montón de complejas declaraciones y banderas if.

 with open(tmptxt, 'w') as outfile: with open(txt, 'r') as infile: rowIter= iter(infile) for row in rowIter: if row.startswith('foo2'): # Start of next section break print row.rstrip(), repr(row) print "foo bar" print row for row in rowIter: print row.rstrip()