Reemplazar y sobrescribir en lugar de agregar

96

Tengo el siguiente código:

import re
#open the xml file for reading:
file = open('path/test.xml','r+')
#convert to string:
data = file.read()
file.write(re.sub(r"<string>ABC</string>(\s+)<string>(.*)</string>",r"<xyz>ABC</xyz>\1<xyz>\2</xyz>",data))
file.close()

donde me gustaría reemplazar el contenido antiguo que está en el archivo con el nuevo contenido. Sin embargo, cuando ejecuto mi código, se agrega el archivo "test.xml", es decir, tengo el contenido antiguo seguido del nuevo contenido "reemplazado". ¿Qué puedo hacer para eliminar las cosas antiguas y conservar solo las nuevas?

Kaly
fuente
Cuando dice "reemplace el contenido antiguo que está en el archivo con el contenido nuevo" , debe leer y transformar el contenido actual data = file.read(). No quiere decir "sobrescribirlo a ciegas sin necesidad de leerlo primero".
smci

Respuestas:

105

Necesita seekal principio del archivo antes de escribir y luego usar file.truncate()si desea reemplazarlo en el lugar:

import re

myfile = "path/test.xml"

with open(myfile, "r+") as f:
    data = f.read()
    f.seek(0)
    f.write(re.sub(r"<string>ABC</string>(\s+)<string>(.*)</string>", r"<xyz>ABC</xyz>\1<xyz>\2</xyz>", data))
    f.truncate()

La otra forma es leer el archivo y luego abrirlo nuevamente con open(myfile, 'w'):

with open(myfile, "r") as f:
    data = f.read()

with open(myfile, "w") as f:
    f.write(re.sub(r"<string>ABC</string>(\s+)<string>(.*)</string>", r"<xyz>ABC</xyz>\1<xyz>\2</xyz>", data))

Ni truncatetampoco open(..., 'w')cambiará el número de inodo del archivo (lo probé dos veces, una con Ubuntu 12.04 NFS y otra con ext4).

Por cierto, esto no está realmente relacionado con Python. El intérprete llama a la API de bajo nivel correspondiente. El método truncate()funciona igual en el lenguaje de programación C: Ver http://man7.org/linux/man-pages/man2/truncate.2.html

guettli
fuente
Neither truncate nor open(..., 'w') will change the inode number of the file¿Por qué es importante?
rok
@rok si el inodo cambia o no, no es relevante en la mayoría de los casos. Solo en casos extremos en los que utilice enlaces físicos, pero le aconsejo que evite los enlaces físicos .
guettli
67
file='path/test.xml' 
with open(file, 'w') as filetowrite:
    filetowrite.write('new content')

Abra el archivo en modo 'w', podrá reemplazar su texto actual y guardar el archivo con contenido nuevo.

Chikku Jacob
fuente
5
Esta es una buena manera de borrar un archivo y escribir algo nuevo en él, pero la pregunta era sobre leer el archivo, modificar el contenido y sobrescribir el original con el nuevo contenido.
Boris
15

Usando truncate(), la solución podría ser

import re
#open the xml file for reading:
with open('path/test.xml','r+') as f:
    #convert to string:
    data = f.read()
    f.seek(0)
    f.write(re.sub(r"<string>ABC</string>(\s+)<string>(.*)</string>",r"<xyz>ABC</xyz>\1<xyz>\2</xyz>",data))
    f.truncate()
serv-inc
fuente
1
seek y truncate !!! No podía entender por qué seekno funcionaba solo.
conner.xyz
2
import os#must import this library
if os.path.exists('TwitterDB.csv'):
        os.remove('TwitterDB.csv') #this deletes the file
else:
        print("The file does not exist")#add this to prevent errors

Tuve un problema similar, y en lugar de sobrescribir mi archivo existente usando los diferentes 'modos', simplemente eliminé el archivo antes de usarlo nuevamente, de modo que sería como si estuviera agregando un nuevo archivo en cada ejecución de mi código .

Nadia Salgado
fuente
1

Consulte Cómo reemplazar cadena en archivo funciona de una manera simple y es una respuesta que funciona conreplace

fin = open("data.txt", "rt")
fout = open("out.txt", "wt")

for line in fin:
    fout.write(line.replace('pyton', 'python'))

fin.close()
fout.close()
Yaacov NNNNM
fuente
0

Usando la biblioteca pathlib de python3 :

import re
from pathlib import Path
import shutil

shutil.copy2("/tmp/test.xml", "/tmp/test.xml.bak") # create backup
filepath = Path("/tmp/test.xml")
content = filepath.read_text()
filepath.write_text(re.sub(r"<string>ABC</string>(\s+)<string>(.*)</string>",r"<xyz>ABC</xyz>\1<xyz>\2</xyz>", content))

Método similar que utiliza un enfoque diferente para las copias de seguridad:

from pathlib import Path

filepath = Path("/tmp/test.xml")
filepath.rename(filepath.with_suffix('.bak')) # different approach to backups
content = filepath.read_text()
filepath.write_text(re.sub(r"<string>ABC</string>(\s+)<string>(.*)</string>",r"<xyz>ABC</xyz>\1<xyz>\2</xyz>", content))
rok
fuente