Substitua e substitua em vez de anexar

102

Eu tenho o seguinte 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()

onde eu gostaria de substituir o conteúdo antigo que está no arquivo pelo novo conteúdo. Porém, quando eu executo meu código, o arquivo "test.xml" é anexado, ou seja, eu tenho o conteúdo antigo seguido pelo novo conteúdo "substituído". O que posso fazer para excluir as coisas antigas e apenas manter as novas?

Kaly
fonte
Quando você diz "substitua o conteúdo antigo que está no arquivo pelo novo conteúdo" , você precisa ler e transformar o conteúdo atual data = file.read(). Você não quer dizer "sobrescrever cegamente sem precisar lê-lo primeiro".
smci

Respostas:

113

Você precisa seekcomeçar o arquivo antes de gravar e, em seguida, usar file.truncate()se quiser fazer a substituição no local:

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()

A outra maneira é ler o arquivo e abri-lo novamente com 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))

Nem truncatenem open(..., 'w')vai mudar o inode número do arquivo (eu testei duas vezes, uma com o Ubuntu 12.04 NFS e uma vez com ext4).

A propósito, isso não está realmente relacionado ao Python. O intérprete chama a API de baixo nível correspondente. O método truncate()funciona da mesma forma na linguagem de programação C: Veja http://man7.org/linux/man-pages/man2/truncate.2.html

Guettli
fonte
Neither truncate nor open(..., 'w') will change the inode number of the filepor que isso é importante?
rok
@rok se o inode muda ou não não é relevante na maioria dos casos. Apenas em casos extremos em que você usa hard-links, mas aconselho evitar hard links .
guettli
71
file='path/test.xml' 
with open(file, 'w') as filetowrite:
    filetowrite.write('new content')

Abra o arquivo no modo 'w', você poderá substituir o texto atual e salvar o arquivo por novos conteúdos.

Chikku Jacob
fonte
6
Esta é uma boa maneira de limpar um arquivo e escrever algo novo nele, mas a questão era sobre ler o arquivo, modificar o conteúdo e sobrescrever o original com o novo conteúdo.
Boris
1
@Boris, qual é o problema em ler o arquivo primeiro e depois usar o código nesta resposta?
Rayhunter
@Rayhunter: é ineficiente
serv-inc
é simples e eficiente, faz o trabalho de maneira perfeita.
Chikku Jacob
16

Usando truncate(), a solução poderia 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
fonte
1
seek e truncate !!! Eu não conseguia descobrir por que seeksozinho não estava funcionando.
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

Tive um problema semelhante e, em vez de substituir meu arquivo existente usando os diferentes 'modos', apenas excluí o arquivo antes de usá-lo novamente, de modo que seria como se estivesse anexando a um novo arquivo a cada execução de meu código .

Nadia Salgado
fonte
1

Veja em Como Substituir String no Arquivo funciona de maneira simples e é uma resposta que funciona comreplace

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
fonte
0

Usando a biblioteca pathlib 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 semelhante usando uma abordagem diferente para backups:

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
fonte