Pregunta

Quiero recorrer el contenido de un archivo de texto y hacer una búsqueda y reemplazar algunas líneas y escribir el resultado en el archivo.Primero podría cargar el archivo completo en la memoria y luego volver a escribirlo, pero probablemente esa 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
¿Fue útil?

Solución

Supongo que algo como esto debería ser suficiente.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)

Otros consejos

La forma más corta probablemente sería utilizar el módulo de entrada de archivos.Por ejemplo, lo siguiente agrega números de línea a un archivo, in situ:

import fileinput

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

Lo que sucede aquí es:

  1. El archivo original se mueve a un archivo de respaldo.
  2. La salida estándar se redirige al archivo original dentro del bucle.
  3. Así cualquier print las declaraciones se vuelven a escribir en el archivo original

fileinput tiene más campanas y silbatos.Por ejemplo, se puede utilizar para operar automáticamente en todos los archivos en sys.args[1:], sin tener que repetirlos explícitamente.A partir de Python 3.2, también proporciona un administrador de contexto conveniente para usar en un with declaración.


Mientras fileinput es excelente para scripts desechables, desconfiaría de usarlo en código real porque es cierto que no es muy legible ni familiar.En el código real (de producción) vale la pena dedicar 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 por completo 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 almacenarlo en la memoria;puede moverlo a un archivo temporal y abrirlo, leerlo línea por línea y volver 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)

Uso de ejemplo:

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 línea a línea de la pregunta original.La función aún puede reemplazar línea a línea

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

Además, re.sub en lugar de reemplazar, permite el reemplazo de expresiones regulares en lugar de solo el reemplazo de texto sin formato.

Leer el archivo como una sola cadena en lugar de línea por línea permite la coincidencia y el 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 desea una función genérica que reemplace cualquier texto con algún 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 pitónica sería utilizar administradores de contexto como el siguiente código:

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 líneas del antiguo al nuevo y reemplácelo antes de escribir las líneas en el nuevo archivo.

Ampliando la respuesta de @Kiran, que estoy de acuerdo es más concisa y Pythonic, esto agrega códecs para admitir 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 coincidiera 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 como se muestra a continuación, 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)
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top