Wie kann ich sicher in eine Datei schreiben?

Stellen Sie sich vor, Sie haben eine Bibliothek für die Arbeit mit einer Art von XML-Datei oder Konfigurationsdatei. Die Bibliothek liest die gesamte Datei in den Speicher und stellt Methoden zur Bearbeitung des Inhalts zur Verfügung. Wenn Sie fertig sind, den Inhalt zu manipulieren, können Sie einen write anrufen write um den Inhalt wieder in Datei zu speichern. Die Frage ist, wie man das auf eine sichere Weise macht.

Das Überschreiben der vorhandenen Datei (das Schreiben in die Originaldatei) ist offensichtlich nicht sicher. Wenn die write fehlschlägt, bevor es fertig ist, kommst du mit einer halben schriftlichen Datei und du hast Daten verloren.

Eine bessere Option wäre, irgendwann in eine temporäre Datei zu schreiben, und wenn die write beendet ist, kopierst du die temporäre Datei in die Originaldatei.

Nun, wenn die Kopie irgendwie scheitert, haben Sie immer noch korrekt Daten in der temporären Datei gespeichert. Und wenn die Kopie erfolgreich ist, kannst du die temporäre Datei entfernen.

Bei POSIX-Systemen schätze ich, dass du den rename Systemaufruf verwenden kannst, der eine atomare Operation ist. Aber wie würdest du das am besten auf einem Windows-System machen? Besonders, wie gehst du damit am besten mit Python ?

Auch gibt es ein anderes Schema für das sichere Schreiben in Dateien?

8 Solutions collect form web for “Wie kann ich sicher in eine Datei schreiben?”

Wenn Sie die Dokumentation von Python sehen, ist es eindeutig erwähnt, dass os.rename () eine atomare Operation ist. Also in Ihrem Fall, das Schreiben von Daten in eine temporäre Datei und dann umbenennen es in die ursprüngliche Datei wäre ziemlich sicher.

Ein anderer Weg könnte so funktionieren:

  • Lassen Sie die Originaldatei abc.xml
  • Erstellen Sie abc.xml.tmp und schreiben Sie neue Daten dazu
  • Umbenennen von abc.xml zu abc.xml.bak
  • Benennen Sie abc.xml.tmp zu abc.xml um
  • Nachdem neue abc.xml richtig eingelegt ist, entfernen Sie abc.xml.bak

Wie Sie sehen können, haben Sie die abc.xml.bak mit Ihnen, die Sie verwenden können, um wiederherzustellen, wenn es irgendwelche Probleme im Zusammenhang mit der tmp-Datei und der Kopie es zurück.

Wenn du pOSIX korrekt sein willst, musst du:

  1. Schreiben Sie in eine temporäre Datei
  2. Flush und fsync die Datei (oder fdatasync )
  3. Umbenennen über die Originaldatei

Beachten Sie, dass das Aufrufen von fsync unvorhersehbare Effekte auf die Leistung hat – Linux auf ext3 kann je nach anderen herausragenden I / O für die Festplatten-I / O ganze Zahlen von Sekunden ablegen.

Beachten Sie, dass die rename keine atomare Operation in POSIX ist – zumindest nicht in Bezug auf Dateidaten, wie Sie erwarten. Allerdings werden die meisten Betriebssysteme und Dateisysteme auf diese Weise funktionieren. Aber es scheint, Sie verpasst die sehr große Linux-Diskussion über Ext4 und Dateisystem Garantien über Atomität. Ich weiß nicht genau, wo zu verknüpfen, aber hier ist ein Start: ext4 und Datenverlust .

Beachten Sie jedoch, dass auf vielen Systemen, umbenennen wird so sicher in der Praxis, wie Sie erwarten. Allerdings ist es in keiner Weise möglich, sowohl – Leistung und Zuverlässigkeit über alle möglichen Linux-Konfiugrationen zu bekommen!

Mit einem Schreiben in eine temporäre Datei, dann eine Umbenennung der temporären Datei, würde man erwarten, dass die Operationen abhängig sind und in der Reihenfolge ausgeführt werden.

Das Problem ist jedoch, dass die meisten, wenn nicht alle Dateisysteme Metadaten und Daten trennen. Eine Umbenennung ist nur Metadaten. Es klingt schrecklich für Sie, aber Dateisysteme Wert Metadaten über Daten (nehmen Sie Journaling in HFS + oder Ext3,4 zum Beispiel)! Der Grund dafür ist, dass Metadaten leichter sind und wenn die Metadaten beschädigt sind, ist das gesamte Dateisystem korrupt – das Dateisystem muss es natürlich selbst bewahren und dann die Daten des Benutzers in dieser Reihenfolge beibehalten.

Ext4 brach die rename Erwartung, als es zuerst herauskam, aber Heuristiken wurden hinzugefügt, um es zu lösen. Das Problem ist nicht eine fehlgeschlagene Umbenennung, sondern eine erfolgreiche Umbenennung. Ext4 kann die Umbenennung erfolgreich registrieren, aber die Dateidaten nicht aufschreiben, wenn ein Crash kurz danach kommt. Das Ergebnis ist dann eine 0-Längen-Datei und weder orignal noch neue Daten.

So kurz gesagt, macht POSIX keine solche Garantie. Lesen Sie den Link Ext4 Artikel für weitere Informationen!

In Win API fand ich ziemlich nette Funktion ReplaceFile , die den Namen schon mit optionaler Unterstützung vorstellt . Es gibt immer einen Weg mit DeleteFile , MoveFile Combo.

Im Allgemeinen, was Sie tun möchten, ist wirklich gut Und ich kann mir kein besseres Schema vorstellen.

Eine vereinfachte Lösung. Verwenden Sie tempfile , um eine temporäre Datei zu erstellen, und wenn das Schreiben erfolgreich ist, benennen Sie die Datei einfach in die ursprüngliche Konfigurationsdatei um.

Zum Sperren einer Datei siehe Portalocker .

Die Standardlösung ist das.

  1. Schreiben Sie eine neue Datei mit einem ähnlichen Namen. X.ext # zum Beispiel.

  2. Wenn diese Datei geschlossen wurde (und vielleicht sogar gelesen und checksummiert), dann sind Sie zwei zwei umbenannt.

    • X.ext (das Original) zu X.ext ~

    • X.ext # (das neue) zu X.ext

  3. (Nur für die verrückten Paranoiden) rufen Sie die OS-Synchronisierungsfunktion auf, um verschmutzte Puffer-Schreibvorgänge zu erzwingen.

Zu keinem Zeitpunkt ist alles verloren oder verderblich. Die einzige Störung kann während der Umbenennen passieren. Aber du hast nichts verloren oder korrumpiert. Das Original ist bis zur endgültigen Umbenennung wiederherstellbar.

Es gibt jetzt einen kodifizierten, reinen Python, und ich wage es zu sagen, Pythonic Lösung, um dies in der Boltons Utility-Bibliothek : boltons.fileutils.atomic_save .

Nur pip install boltons , dann:

 from boltons.fileutils import atomic_save with atomic_save('/path/to/file.txt') as f: f.write('this will only overwrite if it succeeds!\n') 

Es gibt viele praktische Optionen, alle gut dokumentiert . Vollständige Offenlegung, ich bin der Autor von Boltons, aber dieser besondere Teil wurde mit einer Menge Community-Hilfe gebaut. Zögern Sie nicht, eine Notiz zu löschen, wenn etwas unklar ist!

Pro RedGlyphs Vorschlag, ich bin eine Implementierung von ReplaceFile hinzugefügt, die Ctypes verwendet, um auf die Windows-APIs zuzugreifen. Ich habe dies zuerst zu jaraco.windows.api.filesystem hinzugefügt.

 ReplaceFile = windll.kernel32.ReplaceFileW ReplaceFile.restype = BOOL ReplaceFile.argtypes = [ LPWSTR, LPWSTR, LPWSTR, DWORD, LPVOID, LPVOID, ] REPLACEFILE_WRITE_THROUGH = 0x1 REPLACEFILE_IGNORE_MERGE_ERRORS = 0x2 REPLACEFILE_IGNORE_ACL_ERRORS = 0x4 

Ich habe dann das Verhalten mit diesem Skript getestet.

 from jaraco.windows.api.filesystem import ReplaceFile import os open('orig-file', 'w').write('some content') open('replacing-file', 'w').write('new content') ReplaceFile('orig-file', 'replacing-file', 'orig-backup', 0, 0, 0) assert open('orig-file').read() == 'new content' assert open('orig-backup').read() == 'some content' assert not os.path.exists('replacing-file') 

Während dies nur in Windows funktioniert, scheint es eine Menge von schönen Features, die andere ersetzen Routinen würde fehlen. Weitere Informationen finden Sie in den API-Dokumenten .

Sie können das Fileinput-Modul verwenden, um die Sicherung und das In-Place-Schreiben für Sie zu behandeln:

 import fileinput for line in fileinput.input(filename,inplace=True, backup='.bak'): # inplace=True causes the original file to be moved to a backup # standard output is redirected to the original file. # backup='.bak' specifies the extension for the backup file. # manipulate line newline=process(line) print(newline) 

Wenn du den ganzen Inhalt lesen musst, bevor du die Newline schreiben kannst, dann kannst du das zuerst machen und dann den ganzen neuen Inhalt drucken

 newcontents=process(contents) for line in fileinput.input(filename,inplace=True, backup='.bak'): print(newcontents) break 

Wenn das Skript plötzlich endet, wirst du immer noch das Backup haben.

  • Ändern Sie Windows-Verknüpfungen mit Python
  • Stoppen Sie eine gestaute Datei Kopie in Python auf Windows
  • Python kann Prozess nicht mit Process.start () unter Windows starten. PySide Signale
  • Pip-Installation fehlschlägt mit "Verbindungsfehler: [SSL: CERTIFICATE_VERIFY_FAILED] Zertifikat verifiziert fehlgeschlagen (_ssl.c: 598)"
  • Python: Plattformübergreifende Lösung zur Erkennung von physikalischen Nicht-HT-CPUs?
  • Python BaseHTTPServer: Wie bekommst du es zu stoppen?
  • Wie kann man überprüfen, ob eine Windows-Version echt ist oder nicht?
  • Echtzeit lesen von subprocess.stdout auf Windows
  • Python-Konsole in wxPython unter Windows entfernen
  • Windows überträgt keine Befehlszeilenargumente an Python-Programme, die von der Shell ausgeführt werden
  • Verknüpfen meines Windows-Computers mit einem Wifi AP mit Python
  • Python ist die beste Programmiersprache der Welt.