(Geplante) Änderungen an einer Datei vorübergehend speichern und anwenden?

Jacobdabozze

Mitglied
Guten Tag,


Für meine Belegarbeit arbeite ich momentan an einer Anwendung, die Dateien Byte-weise einliest und ausgibt.
Letztendlich handelt es sich um einen erweiterten File-Viewer mit Bearbeitungs-, und später auch Suchoptionen.

appqskqz.png


Das Problem ist, dass ich aus Ressourcengründen nicht in der Lage bin, Datein komplett einzulesen und diese als Byte-Array zu speichern. Hantiert man mit sehr großen Datenmengen, läuft der Arbeitsspeicher voll.
Um dieses Problem zu lösen, lese ich die Daten immer nur Abschnittsweise ein - in diesem Fall 120 Byte pro Aktion.

Wenn auch die Performance um ein vielfaches gesteigert wird, stehe ich nun vor einem schwierigen Problem:
Wie kann ich geplante Änderungen, wie zum Beispiel das Hinzufügen oder Löschen von Daten umsetzen?

Folgende mögliche Varianten habe ich im Kopf:
- eine vollständige Kopie der Datei (Kopie kann sehr viel Speicher fressen, dazu Kopieraufwand, allerdings am leichtesten zu bearbeiten)
- eine "Schatten"-Datei, in welche nur die Daten bis zum geänderten Index kopiert und geändert werden - bei Änderung vorne in der Originaldatei sehr klein, bei Änderung hinten fast so groß wie das Original (potenziell kleiner, potenziell geringerer Kopieraufwand, etwas schwieriger damit zu arbeiten)
- eine Art Register, das alle Änderungen vermerkt - diese Änderungen werden bei jedem Einlesen der Datenblöcke aus der Original-Datei beachtet, also als eine Art Filter, wird beim Speichern in die Original-Datei ebenfalls beachtet (benötigt wenig Speicher, schwierig zu implementieren, *1)

*1 - kann bei vielen Änderungen die Einlesezeit beeinträchtigen, allerdings bietet sich meine Anwendung nicht dafür an, sehr viele Änderungen vorzunehmen


Welche Idee würdet ihr nutzen, oder kennt ihr vielleicht sogar eine noch bessere Lösung?

Ich bedanke mich für jede Hilfe,
Jacob
 
Tatsaechlich klingt das nach einer guten Anwendung fuer RnadomAccessFile, falls du das noch nicht verwendest.

Und da die Aenderungen, also die Anzahl und Groesze dieser, recht ueberschaubar klingt, wuerde ich zur Dritten Variante tendieren. Also dass du dir alle Aenderungen merkst und diese dann "ueberblendend" zur richtigen Datei anzeigst.

Das ist auch nicht wirklich schwierig zu implementieren. Du musst dir nur das Offset und den neuen Wert merken. Jedes mal wenn du einen Wert anzeigst, kontrollierst du ob fuer diesen Offset ein neuer Wert existiert.
 
Das hängt ein wenig von der Art der Dateien ab. Für den allgemeinen Fall würde ich auch zur letzten Variante tendieren - ein "Redo-Log", das beim Öffnen der Datei angewendet wird.
Kannst Du mir dazu einen guten Artikel empfehlen?

Die Grundidee ist mir durchaus klar, nur bin ich mir unsicher, wie ich es schaffe, dass beim Löschen/ Hinzufügen von Bereichen/ Elementen, und später dem Durchsuchen der Datei ohne Speichern diese Änderungen beachtet werden.

Sich im Log überschneidende Ereignisse müssen/ sollten zudem ebenfallse vorher korrigiert werden.
 
Tatsaechlich klingt das nach einer guten Anwendung fuer RnadomAccessFile, falls du das noch nicht verwendest.

Und da die Aenderungen, also die Anzahl und Groesze dieser, recht ueberschaubar klingt, wuerde ich zur Dritten Variante tendieren. Also dass du dir alle Aenderungen merkst und diese dann "ueberblendend" zur richtigen Datei anzeigst.

Das ist auch nicht wirklich schwierig zu implementieren. Du musst dir nur das Offset und den neuen Wert merken. Jedes mal wenn du einen Wert anzeigst, kontrollierst du ob fuer diesen Offset ein neuer Wert existiert.
Tatsächlich arbeite ich bereits mit RandomAccessFile 😀

Leider bin ich mir noch nicht so ganz sicher, wie ich dieses Überblenden angehe.

Bisher habe ich es so gemacht, dass ich ein Byte-Array nutze, welches den entsprechen Abschnitt einliest. Die Länge des Arrays ist dabei von der Anzahl einzulesender Elemente abhängig und wird berechnet:
1) volle Seite -> alle 120 Elemente
2) Seite nicht voll -> 120 Elemente - Anzahl der unbesetzen Elemente

Wenn ich nun allerdings diesen Log führe, dann weiß ich nicht direkt, wie lang das Array werden soll - zumindest sind dann meine vorherigen Rechnungen für die Tonne.

Wie kann man das angehen?
 
Moment, sind das strukturierte Daten, die in Seiten organisiert sind?
Nein, das ist ein Missverständnis- nur zeige ich in meiner grafischen Oberfläche 120 Elemente je Seite an, das ist gemeint.
Wird die Seite gefüllt (das erechne ich aus dem Index und der Länge der Datei), dann werden alle 120 gezeigt, das Array hat eine Länge von 120.
Können keine 120 Elemente ausgelesen werden, dann ist das Array entsprechend kürzer.
 
Also ich wuerde nicht in Seiten arbeiten, sondern in Offsets. In etwas Pseudo-Code:

Java:
public class Change {
    public int offset;
    public byte newValue;
}

public class Diff {
    public SomeMagicClassMaybeMap<Change> changes;
    
    public void addChange(int offset, int newValue) {
        changes.add(new Change(offset, newValue));
    }
    
    public void getChange(int offset) {
        return changes.get(offset);
    }
}

Du kannst dir auch mehrere Bytes je Aenderung merken. Natuerlich musst du dann fuer jedes Byte nachsehen ob es eine Aenderung gibt, aber da du ohnehin nur 120 gleichzeitig anzeigst, ist es komplett vernachlaessigbar.
 
Nein, das ist ein Missverständnis- nur zeige ich in meiner grafischen Oberfläche 120 Elemente je Seite an, das ist gemeint.
Wird die Seite gefüllt (das erechne ich aus dem Index und der Länge der Datei), dann werden alle 120 gezeigt, das Array hat eine Länge von 120.
Können keine 120 Elemente ausgelesen werden, dann ist das Array entsprechend kürzer.
Ok.

Im Prinzip zeichnet das Redo-Log auf, was der Anwender gemacht hat und das wird einfach wieder abgespult. Damit erledigt sich das Problem von Überschneidungen automatisch. Natürlich könnte man hier optimieren und Ereignisse zusammenfassen, aber das wäre etwas für später.

Was direkte Ersetzungen betrifft, wäre die Sache sehr einfach (s. Beispiel von @Robert Zenz), das Problem aber ist, dass Du Bytes entfernen und hinzufügen willst, was sich natürlich auf die Offsets der nachfolgenden Bytes auswirkt.

Daher musst Du das Redo-Log komplett durchgehen und Einträge, die keine wesentlich größeren Offsets betreffen, berücksichtigen. Allerdings wirken sich diese meist nur auf das Offset aus.

Beispiel (mit einem Array): [1,2,3,4,5,6,7,8,9,10], füge ich an Offset 2 [21,22] ein, dann würde sich [1,2,21,22,3,4,5,6,7,8,9,10] ergeben. D. h. das Offset des Eintrags "3" verschiebt sich von 2 nach 4, während das Offset des Eintrags "2" nach wie vor bei 1 bleibt.

Zeige ich jetzt die Bytes ab Offset 8 an, interessieren mich nicht die Werte ([21,22]) der vorherigen Operation, sondern nur, dass sich das Offset um 2 verschoben hat. Dann nämlich muss ich von der gewünschten 8 die 2 abziehen, um aus dem Ur-Array alle Werte ab Offset 6 einzulesen: [7,8,9,10]
 
Ok.

Im Prinzip zeichnet das Redo-Log auf, was der Anwender gemacht hat und das wird einfach wieder abgespult. Damit erledigt sich das Problem von Überschneidungen automatisch. Natürlich könnte man hier optimieren und Ereignisse zusammenfassen, aber das wäre etwas für später.

Was direkte Ersetzungen betrifft, wäre die Sache sehr einfach (s. Beispiel von @Robert Zenz), das Problem aber ist, dass Du Bytes entfernen und hinzufügen willst, was sich natürlich auf die Offsets der nachfolgenden Bytes auswirkt.

Daher musst Du das Redo-Log komplett durchgehen und Einträge, die keine wesentlich größeren Offsets betreffen, berücksichtigen. Allerdings wirken sich diese meist nur auf das Offset aus.

Beispiel (mit einem Array): [1,2,3,4,5,6,7,8,9,10], füge ich an Offset 2 [21,22] ein, dann würde sich [1,2,21,22,3,4,5,6,7,8,9,10] ergeben. D. h. das Offset des Eintrags "3" verschiebt sich von 2 nach 4, während das Offset des Eintrags "2" nach wie vor bei 1 bleibt.

Zeige ich jetzt die Bytes ab Offset 8 an, interessieren mich nicht die Werte ([21,22]) der vorherigen Operation, sondern nur, dass sich das Offset um 2 verschoben hat. Dann nämlich muss ich von der gewünschten 8 die 2 abziehen, um aus dem Ur-Array alle Werte ab Offset 6 einzulesen: [7,8,9,10]
Das ist sehr verständlich.

Eine Frage hätte ich noch, eh ich nun eine wenig sinnvolle Variante eines solchen Logs erstelle:
Wie designed man diesen idealerweise?

Sollte ich den Log in eine temporäre Datei schreiben, oder eine Variante ähnlich der von Robert Zens wählen?
Wie formuliere ich am besten in den Einträgen, dass ein Element gelöscht/ hinzugefügt wurde?

Mir schwirren da einige Ideen durch den Kopf von wegen Befehle wie "Delete" oder ähnliches, die ich beim Einlesen erkenne und anwende.
Doch so wirklich schnell kann das nicht sein.

Leider habe ich damit absolut keine Erfahrungen.

Danke erstmal 😀
 
Sollte ich den Log in eine temporäre Datei schreiben, oder eine Variante ähnlich der von Robert Zens wählen?
Natürlich musst Du das Log in eine Datei schreiben, sonst gibt es ja keinen Sinn, das wäre bei @Robert Zenz nicht anders (davon hat er nur abstrahiert). Temporär ist die Datei insofern, als dass sie irgendwann nicht mehr benötigt wird, nämlich wenn die Änderungen endgültig angenommen oder abgelehnt werden.

Doch so wirklich schnell kann das nicht sein.
Nein, schnell ist das nicht, weil Du Speicher sparen willst/musst. Aber ich würde es einfach mal probieren, manchmal ist man positiv überrascht.

Wie formuliere ich am besten in den Einträgen, dass ein Element gelöscht/ hinzugefügt wurde?
In der Log-Datei? Da würde ich z. B. eine Struktur wie

OffsetLängeBedeutung
04Offset
41Art der Änderung (0 = Ersetzung, 1 = Hinzufügen, 2 = Löschen)
51Anzahl der von der Änderung betroffenen Bytes n
6n / 0 bei Löschungneue/geänderte Bytes, nichts bei Löschung

versuchen. Hier würdest Du halt 6 Bytes lesen. Die restlichen n Bytes (über)liest Du dann einfach im Fall der Fälle.
 

Zurück
Oben