AGB  ·  Datenschutz  ·  Impressum  







Anmelden
Nützliche Links
Registrieren
Thema durchsuchen
Ansicht
Themen-Optionen

Flexible(re) Ladeprozedur

Ein Thema von Master-of-Magic · begonnen am 17. Mär 2007 · letzter Beitrag vom 18. Mär 2007
Antwort Antwort
Seite 1 von 2  1 2      
Master-of-Magic

Registriert seit: 24. Jul 2006
37 Beiträge
 
Delphi 2005 Personal
 
#1

Flexible(re) Ladeprozedur

  Alt 17. Mär 2007, 10:07
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

Folgendes: Ich habe ein Tool das seit einiger Zeit entwickelt wird und wo ab und zu neue Versionen herauskommen (wer hätte es gedacht *g*). Da sich dadurch die Speicherstruktur ändert, muss ich ältere Dateien abwärts kompatibel einlesen können. Meine Datei enthält deshalb einen Integer für die Version und dann mein Array of Record.

Eingelesen hab ich es bisher mit 'nem Filestream und einer case Abfrage nach dem Motto:

Delphi-Quellcode:
case version of
1: Einlesen und an aktuellen Record anpassen
2: Einlesen und an aktuellen Record anpassen
3: Einlesen und an aktuellen Record anpassen
4: Direkt Einlesen, da aktuelle Version
Das Prinzip sollte klar sein. Aber es hat einen großen Nachteil: Für jede neue Version muss ich alle cases anpassen, wenn sich die Speicherstruktur verändert hat.


Daher hab ich mir momentan folgende Variante überlegt:

1. Datei in Filestream (fs) einlesen und Version auslesen
2. fs in Memorystream (ms) kopieren
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;
Ich hoffe der Pseudo-Code ist verständlich und vermittelt meine Idee. Mit der Variante muss ich immer nur einen neuen If-Block für eine neue Speicherstruktur einbauen, was das Handling vereinfacht. Ältere Versionen steigen entsprechend früher in den Code ein und werden angepasst.
Allerdings muss ich mich so mit drei Streams herumschlagen die die Datei enthalten -> nicht gerade Ressourcenfreundlich.

Hat mir da jemand Tipps/Verbesserungsvorschläge? Die Datei hat zwar nur 50K, aber hier gehts ums Prinzip
Oder gibts einen völlig anderen Weg?
  Mit Zitat antworten Zitat
Der_Unwissende

Registriert seit: 13. Dez 2003
Ort: Berlin
1.756 Beiträge
 
#2

Re: Flexible(re) Ladeprozedur

  Alt 17. Mär 2007, 10:55
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:
  1. Auslesen eines TypX-Arrays aus einem Stream
  2. 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
  Mit Zitat antworten Zitat
Benutzerbild von Jelly
Jelly

Registriert seit: 11. Apr 2003
Ort: Moestroff (Luxemburg)
3.741 Beiträge
 
Delphi 2007 Professional
 
#3

Re: Flexible(re) Ladeprozedur

  Alt 17. Mär 2007, 12:34
Warum hat Gott die objektorientierte Programmierung erfunden.

Erstell Dir zu jeder Version eine Klasse, die sich ums Kovertieren kümmert. Willst Du ganz generisch halten, und dir die if-Abfragen (bzw. deine Case Strukur was genau das gleiche ist nur in grün), so kannst Du mit Metaklassen arbeiten. Ich hatte diese Problematik auch mal erfragt, und in diesem Thread wurd ziemlich gut erklärt, wie du sowas handeln kannst.

Im Grunde wird aufgrund der Version, die als Integer irgendwie vorliegen könnte, die entsprechende richtige Klasse instanziert.
  Mit Zitat antworten Zitat
Master-of-Magic

Registriert seit: 24. Jul 2006
37 Beiträge
 
Delphi 2005 Personal
 
#4

Re: Flexible(re) Ladeprozedur

  Alt 17. Mär 2007, 13:51
Zitat von Der_Unwissende:
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?
Ja, ich hab mir davon einen Geschwindigkeitsvorteil erhofft, wenn im Ram gearbeitet wird, anstatt die HD zu belasten. Im Prinzip ist der Streamtyp egal.

Zitat:
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?
Ja und Ja. Allerdings ist das Ganze etwas kompleer als nur einfach ein paar Records. Die Struktur sieht grob so aus:
Delphi-Quellcode:
type
  TPlaneten = packed Record
    [statische Arrays, Strings und Daten]
  end;
  TAccount = packed Record
    Planet: array[0..12] of TPlaneten;
    [statische Arrays, Strings und Daten]
  end;
var
  Account:Array[0..12] of TAccount;
Gespeichert wird einfach das Account-Array. Ändern tun sich in den Versionen meist die Anzahl der Account und die Inhalte des TPlaneten-Records.

Zitat:
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.
Ich seh da aber ein Problem: Ich muss für jede Version das komplette Record erstellen. Würde das nicht heißen, ich hab für jede Version den Speicher reserviert? Mir scheint, dass das noch aufwändiger und Speicher fressender ist, als meine bisherige Methode ...

@Jelly
Zitat:
Warum hat Gott die objektorientierte Programmierung erfunden.
Ach Gott war das? Ich dachte, das wäre eine Nebenwirkung der Erfindung von C++ & Co. gewesen ...

Zitat:
Erstell Dir zu jeder Version eine Klasse, die sich ums Kovertieren kümmert. Willst Du ganz generisch halten, und dir die if-Abfragen (bzw. deine Case Strukur was genau das gleiche ist nur in grün), so kannst Du mit Metaklassen arbeiten. Ich hatte diese Problematik auch mal erfragt, und in diesem Thread wurd ziemlich gut erklärt, wie du sowas handeln kannst.
Da muss ich leider sagen, dass ich noch nie bewusst mit Klassen gearbeitet hab und daher auch keine Ahnung davon hab. Hast du mir da ein kleines Tutorial bzw. kannst mir ein Beispiel dazu geben?
Weil bisher klingt das auch so, als ob ich für jede Version das Array of Record erstellen müsste ... und das scheint mir mehr Aufwand, als mein bisheriger Weg.
  Mit Zitat antworten Zitat
Benutzerbild von Jelly
Jelly

Registriert seit: 11. Apr 2003
Ort: Moestroff (Luxemburg)
3.741 Beiträge
 
Delphi 2007 Professional
 
#5

Re: Flexible(re) Ladeprozedur

  Alt 17. Mär 2007, 14:13
Kuck mal auf Luckie's Homepage, da gibts was zu Klassen.
Prinzipiell wirst du gleiche Sachen in einer Basisklasse definieren, und versionsabhängige Sachen in davon abgeleiteten Klassen. Etwa so, in sehr knapper Form:

Delphi-Quellcode:
type
  TBasis = class
  public
      Planetname : string ;

      // ...

      constructor Create (Filename : string) ;

      procedure Load ; virtual ;

  end;

  TPluto = class (TBasis)
  public
      procedure Load ; override ;
  end;
  TVenus = class (TBasis)
  public
      procedure Load ; override ;
  end;
Nutzen geht dann so:

Delphi-Quellcode:
var
  Pluto, Venus : TBasis ;
begin
     Pluto := TPluto.Create ('file.txt') ;
     Venus := TVenus.Create ('file.txt') ;
     Pluto.Load ;
     Venus.Load ;
end.
Dadurch wird dann jeweils die korrekte Load Prozedur aufgerufen.
  Mit Zitat antworten Zitat
Master-of-Magic

Registriert seit: 24. Jul 2006
37 Beiträge
 
Delphi 2005 Personal
 
#6

Re: Flexible(re) Ladeprozedur

  Alt 17. Mär 2007, 15:32
Mit den Planetennamen in deinem Beispiel meintest du die verschiedenen Versionen meiner Datei, oder? Weil das ist etwas unglücklich gewählt - im Prinzip verändern sich ja nicht nur die Planeten, sonder auch mal die Accounts bzw. es kommen neue Accounts (+Planeten) dazu.

Wenn ich dein Beispiel (und Luckies Tutorial) recht verstanden habe, wäre ich mit der Lösung zwar elegant, aber wieder am Anfang. Weil die einzelnen Konvertierungen werden ja nicht mehr nacheinander, sondern wieder 'auf einmal' ausgeführt. Und somit müsste ich bei einer neuen Version wieder alle .Load Prozeduren ändern, damit sie an das aktuelle record angepasst werden.
  Mit Zitat antworten Zitat
Benutzerbild von Jelly
Jelly

Registriert seit: 11. Apr 2003
Ort: Moestroff (Luxemburg)
3.741 Beiträge
 
Delphi 2007 Professional
 
#7

Re: Flexible(re) Ladeprozedur

  Alt 17. Mär 2007, 16:35
Natürlich musst du die load Prozedur bei einer neuen Version neu schreiben. Wie soll das auch anders gehen
  Mit Zitat antworten Zitat
Master-of-Magic

Registriert seit: 24. Jul 2006
37 Beiträge
 
Delphi 2005 Personal
 
#8

Re: Flexible(re) Ladeprozedur

  Alt 17. Mär 2007, 17:01
Ich glaub du hast mich falsch verstanden. Sicher muss ich die Load Prozedur erweitern. Aber in meiner obigen Variante muss ich nur die If-Abfrage und das Einlesen der neuen Version hinzufügen.

Bei meiner alten und bei deiner Variante - sofern ich dich recht verstanden habe - müsste ich alle Load-Procedures für jede Version updaten... verstehst du mein Problem?
  Mit Zitat antworten Zitat
Benutzerbild von Jelly
Jelly

Registriert seit: 11. Apr 2003
Ort: Moestroff (Luxemburg)
3.741 Beiträge
 
Delphi 2007 Professional
 
#9

Re: Flexible(re) Ladeprozedur

  Alt 17. Mär 2007, 17:51
Gemeinsamheit zwischen den Version kannst du in der Basisklasse implementieren. Da liegt ja gerade der Vorteil von OOP.
  Mit Zitat antworten Zitat
Master-of-Magic

Registriert seit: 24. Jul 2006
37 Beiträge
 
Delphi 2005 Personal
 
#10

Re: Flexible(re) Ladeprozedur

  Alt 17. Mär 2007, 17:57
Die Gemeinsamkeiten kann ich aber nicht voraussehen. Mal ändert sich ein Variablentyp, dann wird hier mal ein Array erweitert oder dort ein ganz neues Array eingefügt ...

Im Prinzip sind Klassen eine gute Idee für die normale Verwendung in meinem Tool. Aber für die Ladeprozedur scheinen sie mir ungeeignet, da ich immer irgendwie eingeschränkt bin.
  Mit Zitat antworten Zitat
Antwort Antwort
Seite 1 von 2  1 2      


Forumregeln

Es ist dir nicht erlaubt, neue Themen zu verfassen.
Es ist dir nicht erlaubt, auf Beiträge zu antworten.
Es ist dir nicht erlaubt, Anhänge hochzuladen.
Es ist dir nicht erlaubt, deine Beiträge zu bearbeiten.

BB-Code ist an.
Smileys sind an.
[IMG] Code ist an.
HTML-Code ist aus.
Trackbacks are an
Pingbacks are an
Refbacks are aus

Gehe zu:

Impressum · AGB · Datenschutz · Nach oben
Alle Zeitangaben in WEZ +1. Es ist jetzt 02:19 Uhr.
Powered by vBulletin® Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz