Buscar y reemplazar una línea en un archivo en Python

Quiero recorrer el contenido de un archivo de texto y hacer una búsqueda y reemplazo en algunas líneas y escribir el resultado de nuevo en el archivo. Primero podría cargar todo el archivo en la memoria y luego escribirlo, pero probablemente no sea la mejor manera de hacerlo.

¿Cuál es la mejor manera de hacerlo, dentro del siguiente código?

f = open(file) for line in f: if line.contains('foo'): newline = line.replace('foo', 'bar') # how to write this newline back to the file 

Supongo que algo como esto debería hacerlo. Básicamente, escribe el contenido en un archivo nuevo y reemplaza el archivo antiguo con el archivo nuevo:

 from tempfile import mkstemp from shutil import move from os import fdopen, remove def replace(file_path, pattern, subst): #Create temp file fh, abs_path = mkstemp() with fdopen(fh,'w') as new_file: with open(file_path) as old_file: for line in old_file: new_file.write(line.replace(pattern, subst)) #Remove original file remove(file_path) #Move new file move(abs_path, file_path) 

La forma más corta probablemente sería usar el módulo fileinput . Por ejemplo, lo siguiente agrega números de línea a un archivo, en el lugar:

 import fileinput for line in fileinput.input("test.txt", inplace=True): print "%d: %s" % (fileinput.filelineno(), line), 

Lo que pasa aquí es:

  1. El archivo original se mueve a un archivo de copia de seguridad
  2. La salida estándar se redirige al archivo original dentro del bucle
  3. Por lo tanto, cualquier statement print escribe en el archivo original.

fileinput tiene más campanas y silbidos. Por ejemplo, puede usarse para operar automáticamente en todos los archivos en sys.args[1:] , sin que tenga que iterar sobre ellos explícitamente. Comenzando con Python 3.2, también proporciona un administrador de contexto conveniente para usar en una statement with .


Si bien la entrada de fileinput es excelente para los scripts desechables, desconfiaría de usarlos en código real porque, ciertamente, no es muy legible o familiar. En el código real (producción), vale la pena gastar solo unas pocas líneas más de código para hacer que el proceso sea explícito y así hacer que el código sea legible.

Hay dos opciones:

  1. El archivo no es demasiado grande, y puede leerlo completamente en la memoria. Luego cierre el archivo, vuelva a abrirlo en modo de escritura y vuelva a escribir el contenido modificado.
  2. El archivo es demasiado grande para ser almacenado en la memoria; puede moverlo a un archivo temporal y abrirlo, leyéndolo línea por línea, volviendo a escribir en el archivo original. Tenga en cuenta que esto requiere el doble de almacenamiento.

Aquí hay otro ejemplo que se probó, y coincidirá con los patrones de búsqueda y reemplazo:

 import fileinput import sys def replaceAll(file,searchExp,replaceExp): for line in fileinput.input(file, inplace=1): if searchExp in line: line = line.replace(searchExp,replaceExp) sys.stdout.write(line) 

Ejemplo de uso:

 replaceAll("/fooBar.txt","Hello\sWorld!$","Goodbye\sWorld.") 

Esto debería funcionar: (edición in situ)

 import fileinput # Does a list of files, and # redirects STDOUT to the file in question for line in fileinput.input(files, inplace = 1): print line.replace("foo", "bar"), 

Basado en la respuesta de Thomas Watnedal. Sin embargo, esto no responde exactamente a la parte de línea a línea de la pregunta original. La función todavía se puede reemplazar línea a línea

Esta implementación reemplaza el contenido del archivo sin usar archivos temporales, como consecuencia, los permisos de los archivos permanecen sin cambios.

También re.sub en lugar de reemplazar, permite el reemplazo de expresiones regulares en lugar del reemplazo de texto sin formato solamente.

Leer el archivo como una sola cadena en lugar de línea por línea permite la coincidencia y reemplazo de varias líneas.

 import re def replace(file, pattern, subst): # Read contents from file as a single string file_handle = open(file, 'r') file_string = file_handle.read() file_handle.close() # Use RE package to allow for replacement (also allowing for (multiline) REGEX) file_string = (re.sub(pattern, subst, file_string)) # Write contents to file. # Using mode 'w' truncates the file. file_handle = open(file, 'w') file_handle.write(file_string) file_handle.close() 

Como sugiere Lassevk, escriba el nuevo archivo a medida que avanza, aquí hay un código de ejemplo:

 fin = open("a.txt") fout = open("b.txt", "wt") for line in fin: fout.write( line.replace('foo', 'bar') ) fin.close() fout.close() 

Si quieres una función genérica que reemplace cualquier texto con otro texto, esta es probablemente la mejor manera de hacerlo, especialmente si eres fanático de las expresiones regulares:

 import re def replace( filePath, text, subs, flags=0 ): with open( filePath, "r+" ) as file: fileContents = file.read() textPattern = re.compile( re.escape( text ), flags ) fileContents = textPattern.sub( subs, fileContents ) file.seek( 0 ) file.truncate() file.write( fileContents ) 

Una forma más pythonica sería usar administradores de contexto como el código a continuación:

 from tempfile import mkstemp from shutil import move from os import remove def replace(source_file_path, pattern, substring): fh, target_file_path = mkstemp() with open(target_file_path, 'w') as target_file: with open(source_file_path, 'r') as source_file: for line in source_file: target_file.write(line.replace(pattern, substring)) remove(source_file_path) move(target_file_path, source_file_path) 

Puedes encontrar el fragmento completo aquí .

Cree un nuevo archivo, copie las líneas del antiguo al nuevo y haga el reemplazo antes de escribir las líneas en el nuevo archivo.

Ampliando la respuesta de @Kiran, que estoy de acuerdo es más breve y Pythonic, esto agrega códecs para apoyar la lectura y escritura de UTF-8:

 import codecs from tempfile import mkstemp from shutil import move from os import remove def replace(source_file_path, pattern, substring): fh, target_file_path = mkstemp() with codecs.open(target_file_path, 'w', 'utf-8') as target_file: with codecs.open(source_file_path, 'r', 'utf-8') as source_file: for line in source_file: target_file.write(line.replace(pattern, substring)) remove(source_file_path) move(target_file_path, source_file_path) 

Usando la respuesta de hamishmcn como plantilla, pude buscar una línea en un archivo que coincida con mi expresión regular y reemplazarla con una cadena vacía.

 import re fin = open("in.txt", 'r') # in file fout = open("out.txt", 'w') # out file for line in fin: p = re.compile('[-][0-9]*[.][0-9]*[,]|[-][0-9]*[,]') # pattern newline = p.sub('',line) # replace matching strings with empty string print newline fout.write(newline) fin.close() fout.close() 

Si elimina la sangría en el siguiente ejemplo, buscará y reemplazará en varias líneas. Vea a continuación por ejemplo.

 def replace(file, pattern, subst): #Create temp file fh, abs_path = mkstemp() print fh, abs_path new_file = open(abs_path,'w') old_file = open(file) for line in old_file: new_file.write(line.replace(pattern, subst)) #close temp file new_file.close() close(fh) old_file.close() #Remove original file remove(file) #Move new file move(abs_path, file) 

Para usuarios de Linux:

 import os os.system('sed -i \'s/foo/bar/\' '+file_path)