Zastąp i nadpisz zamiast dołączania

102

Mam następujący kod:

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

gdzie chciałbym zastąpić starą zawartość znajdującą się w pliku nową zawartością. Jednak gdy wykonuję kod, dołączany jest plik „test.xml”, tzn. Mam starą zawartość za nową zawartością „zastąpioną”. Co mogę zrobić, aby usunąć stare rzeczy i zachować tylko nowe?

Kaly
źródło
Kiedy mówisz „Zastąp starą treść znajdującą się w pliku nową zawartością” , musisz przeczytać i przekształcić obecną zawartość data = file.read(). Nie masz na myśli „ślepego nadpisania bez konieczności wcześniejszego przeczytania”.
smci

Odpowiedzi:

113

Musisz seekzacząć plik przed zapisaniem, a następnie użyć, file.truncate()jeśli chcesz wykonać zamianę w miejscu:

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

Innym sposobem jest odczytanie pliku, a następnie ponowne otwarcie go za pomocą 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))

Ani truncateteż open(..., 'w')nie zmieni numeru i- węzła pliku (testowałem dwukrotnie, raz z Ubuntu 12.04 NFS i raz z ext4).

Nawiasem mówiąc, nie jest to tak naprawdę związane z Pythonem. Interpreter wywołuje odpowiedni interfejs API niskiego poziomu. Metoda truncate()działa tak samo w języku programowania C: patrz http://man7.org/linux/man-pages/man2/truncate.2.html

guettli
źródło
Neither truncate nor open(..., 'w') will change the inode number of the fileDlaczego to jest ważne?
rok
@rok, jeśli i-węzeł zmienia się lub nie, w większości przypadków nie ma znaczenia. Tylko w skrajnych przypadkach, gdy używasz twardych linków, ale radzę unikać twardych linków .
guettli
71
file='path/test.xml' 
with open(file, 'w') as filetowrite:
    filetowrite.write('new content')

Otwórz plik w trybie 'w', będziesz mógł zamienić jego obecny tekst i zapisać plik z nową zawartością.

Chikku Jacob
źródło
6
To dobry sposób na wyczyszczenie pliku i zapisanie w nim czegoś nowego, ale chodziło o odczytanie pliku, modyfikację zawartości i nadpisanie oryginału nową zawartością.
Boris
1
@Boris, jaki jest problem z odczytaniem pliku najpierw, a następnie użyciem kodu w tej odpowiedzi?
Rayhunter
@Rayhunter: to nieefektywne
serv-inc
to proste i wydajne, wykonuje swoją pracę w doskonały sposób.
Chikku Jacob
16

Używając truncate(), rozwiązaniem może być

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
źródło
1
seek i truncate !!! Nie mogłem zrozumieć, dlaczego seeksam nie działa.
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

Miałem podobny problem i zamiast nadpisywać mój istniejący plik przy użyciu różnych „trybów”, po prostu usunąłem plik przed ponownym użyciem, aby wyglądało to tak, jakbym dołączał do nowego pliku przy każdym uruchomieniu mojego kodu .

Nadia Salgado
źródło
1

Zobacz artykuł Jak zamienić ciąg w pliku działa w prosty sposób i jest odpowiedzią, która działa zreplace

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
źródło
0

Korzystanie z biblioteki 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))

Podobna metoda wykorzystująca inne podejście do kopii zapasowych:

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
źródło