texto - gestion de archivos en python
Buscar y reemplazar una lĂnea en un archivo en Python (13)
Quiero hacer un bucle sobre 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 hacer esto, 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
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)
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.")
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()
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.
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"),
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:
- El archivo original se mueve a un archivo de copia de seguridad
- La salida estándar se redirige al archivo original dentro del bucle
- Por lo tanto, cualquier declaración
print
escribe en el archivo original
fileinput
tiene más campanas y silbidos. Por ejemplo, puede utilizarse 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 declaración 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 fácil de leer o no me resulta familiar. En el código real (producción), vale la pena gastar solo unas pocas líneas más de código para hacer el proceso explícito y así hacer que el código sea legible.
Hay dos opciones:
- 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.
- 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.
Para usuarios de Linux:
import os
os.system(''sed -i /'s/foo/bar//' ''+file_path)
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)
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 )
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)
Una forma más pitónica sería utilizar 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 here .
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()