Zitat von
Master-of-Magic:
Erstmal hoff ich, dass ich die richtige Spart erwischt hab. Es gibt ja kein extra Unterforum für Dateien & Co - falls doch, dann sponsort mir eine Brille
Hi,
ohne dass ich jetzt irgendeinen Einfluss auf die Einordnung durch die Moderatoren habe (außer vielleicht ich kann mich glaubhaft über deine Einordnung beschweren), ich würde nicht sagen dass es so direkt etwas mit dem Windows
API zu tun hat (oder übersehe ich die
API Zugriffe?). Fände ObjectPascal/Delphi-Language oder sogar sonstige Fragen passender.
Aber ist ja auch egal!
Zitat von
Master-of-Magic:
Daher hab ich mir momentan folgende Variante überlegt:
1. Datei in Filestream (fs) einlesen und Version auslesen
2. fs in Memorystream (ms) kopieren
Ok, kannst Du diesen Teil denn schon begründen? Also an sich ist die Idee sicherlich, dass Du schneller arbeiten würdest, wenn Du statt einem FileStream einen MemoryStream verwendest, ist
imho nicht so. Schon der FileStream scheint automatisch einen Puffer für das Lesen/Schreiben zu verwenden und ist mit dem deutlich schneller unterwegs als dein MemoryStream (der immer komplett im Speicher landet). Das Problem des MemoryStream ist, dass Du mit dem halt einen sehr sehr großen Bereich im Speicher belegen kannst (bei 50 KByte natürlich nicht, aber bei wirklich großen Dateien). Somit landet ein Teil von ihm schnell im Virtuellen Speicher und/oder verdrängt andere Daten sinnlos aus dem Hauptspeicher.
Hier solltest Du schauen, ob Du wirklich Geschwindigkeit durch die Verwendung eines MemoryStreams gewinnst. Schon das Umkopieren dürfte allerdings mehr Zeit kosten. Kann natürlich auch sein, dass Du es aus einem anderen Grund machst, aber eigentlich sollte alles auch mit einem FileStream möglich sein.
Zitat von
Master-of-Magic:
3.
Delphi-Quellcode:
if version=1 then
begin
ms an Record von Version 2 (!) anpassen und in Temp-Memorystream (temp) kopieren
temp wieder nach ms zurückkopieren
version auf 2 setzen
end;
if version=2 then
begin
ms an Record von Version 3 (!) anpassen und in Temp-Memorystream (temp) kopieren
temp wieder nach ms zurückkopieren
version auf 3 setzen
end;
if version=3 then
begin
ms an Record von Version 4 (!) anpassen und in Temp-Memorystream (temp) kopieren
temp wieder nach ms zurückkopieren
version auf 4 setzen
end;
[...usw...]
if version=4 then
begin
ms direkt in Speicher einlesen
end;
Hm, auch hier würde ich denken, dass das umkopieren recht viel Zeit kostet. An sich ist die Frage, wie deine Daten überhaupt gespeichert sind ganz Interessant. Liegen die Records denn direkt hintereinander in der Datei? Und weißt Du wieviele es sind?
Wenn dies der Fall ist, dann kannst Du sehr effezient einfach den ganzen Block aller Datensätze in ein entsprechendes Array einlesen. Je nach Versionsnummer erstellst Du einfach ein dyn. Array der entsprechenden Größe und des entsprechenden Typs.
Die restliche Arbeit kannst Du dann einfach auf einem solchen Array ausführen. Deine Idee immer die Anpassung an die nächste Version vorzunehmen solltest Du natürlich genauso beibehalten. Der Unterschied liegt dann nur darin, dass Du diese Datensätze nicht extra aus einem Stream extrahierst und wieder dorthin zurückschreibst, sondern gleich mit einem festen Datentypen arbeitest. Das sollte die Arbeit deutlich vereinfachen.
An sich muss jede Version dann zwei Methoden anbieten:
- Auslesen eines TypX-Arrays aus einem Stream
- Konvertieren der Vorgängerversion (X-1) in die eigene (vorher weißt Du ja nicht was nötig ist).
Und als Pseudocode:
Delphi-Quellcode:
type
TTyp1 = record
...
end;
TTyp1Array = Array of TTyp1;
TTyp2 = record
...
end;
TTyp2Array = Array of TTyp2;
TTyp3 = record
...
end;
TTyp3Array = Array of TTyp3;
TAktuellerTyp = TTyp3;
TAktuellerTypArray = Array of TAktuellerTyp;
function loadFromStream(const Stream: TStream): TAktuellerTypArray;
var versionsNummer: Integer;
begin
...
case versionsNummer of
1: result := convert(readTyp1(Stream));
2: result := convert(readTyp2(Stream));
3: result := convert(readTyp3(Stream));
end;
end;
function readTyp1(const Stream: TStream): TTyp1Array;
begin
// ist klar!
end;
function readTyp2(const Stream: TStream): TTyp2Array;
begin
// ist klar!
end;
//...
function convert(var typ1Array: TTyp1Array): TAktuellerTypArray;
var typ2Array: TTyp2Array;
begin
// Umwandlung von typ1Array in ein Typ2 Array
// ....
// typ1Array wird nicht mehr benötigt, Speicher frei geben!
setLength(typ1Array, 0);
finalize(typ1Array);
result := convert(typ2Array);
end;
// ....
function convert(const typ3Array: TTyp3Array): TAktuellerTypArray;
begin
result := typ3Array;
end;
So ungefähr würde ich es Dir vorschlagen. Die Idee ist hoffentlich auch hier klar. Du nimmst einfach einen Alias für den aktuellen Datentypen und gibt ein Array von diesem Typen als Ergebnis des Ladens zurück.
Für die aktuelle Version muss also nur das übergebene Array zurückgegeben werden. Kommt eine neue Version hinzu, erstellst Du den entsprechenden neuen Datentypen und änderst den Alias TAktuellerTyp entsprechend ab. Zudem musst Du convert für diesen Typen überladen und das convert der Vorgängerversion anpassen. Das Laden gehört dann natürlich auch noch in die case-Anweisung, aber an sich halten sich (hoffentlich) die Änderungen in Grenzen.
Wie gesagt, der eigentliche Unterschied zu deinem Ansatz liegt hier nur darin, dass Du gleich mit Datentypen arbeitest und nicht den Umweg über zwei Streams gehst.
Gruß Der Unwissende