Autor Beitrag
Metschu
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 135

Windows XP SP2 Home
Delphi 7; Delphi XE2-Starter
BeitragVerfasst: So 16.05.10 14:34 
Hallo zusammen!

Ich speichere Daten in einer TRecord Datei.
Wenn ich nun aufgrund eines Updates neue Variablen in TRecord aufnehme, werden mir ja logischerweise beim laden von alten Dateien die Variablen durcheinander gebracht.

Derzeit schreibe ich ein seperates Tool, um die Dateien umzuwandel und muß die dann alle einzeln auswählen.

Habe ich beim speichern die Möglichkeit, der Datei eine Versionsnummer zuzuordnen?
So kann man beim Laden die Version der Datei prüfen und wenn diese älter ist, automatisch innerhalb der Software anpassen ohne das seperate Tool.

Oder gibts auch ganz andere, einfachere Wege?

Danke schonmal.

Gruß

Torsten
jaenicke
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 19326
Erhaltene Danke: 1749

W11 x64 (Chrome, Edge)
Delphi 12 Pro, C# (VS 2022), JS/HTML, Java (NB), PHP, Lazarus
BeitragVerfasst: So 16.05.10 15:54 
Normalerweise gibt es dafür einen Dateikopf, in dem die Informationen zum Format usw. stehen.

Wenn du natürlich aus irgendwelchen Gründen einen file of TXyRecord hast, dann geht das nicht. Dann muss der Benutzer die Umwandlung anstoßen. Würde ich nicht so machen. :nixweiss:

Ich habe immer die einfachste Lösung genommen. Eigenes Dateiformat, optimiert für schnelle Zugriffe, da ich zusätzliche Navigationsinfos einbinden kann. Entsprechende Klassen mit LoadFromFile und SaveToFile, schon funktioniert alles. Beim Laden wird an Hand der Versionsangabe ggf. intern eine Konvertierung des Streams durchgeführt, fertig.
Zusätzlich gibt es bei mir reservierte Nullfelder, die dann in späteren Versionen eine Erweiterung des Formats erlauben ohne dabei die Verarbeitbarkeit in alten Versionen zu verbauen (nur neue Informationen sind dort dann eben nicht sichtbar).
dummzeuch
ontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic starofftopic star
Beiträge: 593
Erhaltene Danke: 5


Delphi 5 ent, Delphi 6 bis Delphi XE8 pro
BeitragVerfasst: So 16.05.10 15:57 
user profile iconMetschu hat folgendes geschrieben Zum zitierten Posting springen:

Ich speichere Daten in einer TRecord Datei.
Wenn ich nun aufgrund eines Updates neue Variablen in TRecord aufnehme, werden mir ja logischerweise beim laden von alten Dateien die Variablen durcheinander gebracht.

Derzeit schreibe ich ein seperates Tool, um die Dateien umzuwandel und muß die dann alle einzeln auswählen.

Habe ich beim speichern die Möglichkeit, der Datei eine Versionsnummer zuzuordnen?
So kann man beim Laden die Version der Datei prüfen und wenn diese älter ist, automatisch innerhalb der Software anpassen ohne das seperate Tool.


Genau deshalb verwendet man bei komplexeren Programmen Datenbanken bzw. Tabellen, da gibt es jeweils die Moeglichkeit, dass das Programm prueft, welche Felder mit welchem Datentyp es gibt.

Allerdings haben Datenbanken gegenueber Records mindestens zwei Nachteile:
1. Sie sind deutlich langsamer
2. Sie sind aufwaendiger zu programmieren

Julian Bucknall beschreibt in seinem Buch "Tomes of Delphi: Algorithms and Data Structures" eine TtdRecordFile Klasse, die die von Dir gewuenschten Optionen bietet. Das funktioniert so, dass er am Anfang jeder Datei eine bestimmte Anzahl Bytes reserviert, in der er Verwaltungsinformationen ablegt, u.a. die Groesse des in der Datei gespeicherten Records. Dann gibt es je eine Read und Write Methode, die anhand eines Index ein Seek auf die Datei macht und einen Record liest bzw. schreibt.
(Das Buch kann ich uebrigens sehr emfpehlen.)

Im Prinzip laeuft das so (ungetestet, compiliert vermutlich noch nicht mal):

ausblenden volle Höhe Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
34:
35:
36:
37:
38:
39:
40:
41:
42:
43:
44:
45:
46:
47:
48:
49:
50:
51:
52:
53:
54:
55:
56:
57:
58:
59:
60:
61:
62:
63:
64:
65:
66:
67:
68:
69:
70:
71:
72:
73:
74:
75:
type
  TRecordFile = class
  private
    FHeaderSize: integer;
    FRecordSize: integer;
    FStream: TFileStream;
  public
    constructor Create(const _Filename: string; _HeaderSize: integer; _RecordSize: integer);
    destructor Destroy; override;
    procedure WriteHeader(var _Header);
    procedure ReadHeader(var _Header);
    procedure Write(_Idx: integer; var _Record);
    procedure Read(_Idx: integer; var _Record);
  end;

constructor TRecordFile.Create(const _Filename: string; _HeaderSize: integer; _RecordSize: integer);
begin
  inherited Create;
  FHeaderSize := _HeaderSize;
  FRecordSize := _RecordSize;
  FStream := TFileStream.Create(Filename);
end;

destructor TRecordFile.Destroy; 
begin 
  FreeAndNil(FStream);
  inherited Destroy; 
end;

procedure TRecordFile.Write(_Idx: integer; var _Record);
begin
  FStream.Seek(FHeaderSize + _Idx * FRecordSize);
  FStream.Write(_Record, FRecordSize);
end;

procedure TRecordFile.Read(_Idx: integer; var _Record);
begin
  FStream.Seek(FHeaderSize + _Idx * FRecordSize);
  FStream.Read(_Record, FRecordSize);
end;

procedure TRecordFile.WriteHeader(var _Header);
begin
  FStream.Seek(0);
  FStream.Write(_Header, FHeaderSize);
end;

procedure TRecordFile.ReadHeader(var _Header);
begin
  FStream.Seek(0);
  FStream.Read(_Header, FHeaderSize);
end;

// Benutzung:
var
  st: TRecordFile;
  Header: record
            Version: integer;
          end;
  Rec: record
         // Dein Datenrecord hier
       end;
begin
  st := TRecordFile.Create('c:\test.dat', SizeOf(Header), SizeOf(Rec));
  try
    st.ReadHeader(Header);
    if Header.Version <> ErwarteteVersion then begin
      KonvertierenUndNeuOeffnen(st);
    end;
    // ersten Record lesen ...
    st.Read(0, Rec);
    // usw.
  finally
    FreeAndNil(st);
  end;


Ein Kollege (Hi DS) hat darauf basierend eine Dateistruktur entwickelt, die sich selbst beschreibt, also den darin gespeicherten Record mit seinen Feldern und diverse andere Angaben, so dass ein Programm die Daten lesen kann, ohne zu wissen, welche Records gespeichert sind. Aber sowas kompliziertes brauchst Du ja erstmal gar nicht, es reicht ja, wenn Du im Header eine Versionsinfo stehen hast.

twm
jaenicke
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 19326
Erhaltene Danke: 1749

W11 x64 (Chrome, Edge)
Delphi 12 Pro, C# (VS 2022), JS/HTML, Java (NB), PHP, Lazarus
BeitragVerfasst: So 16.05.10 16:11 
Wobei mir eine Notlösung für Records einfällt: Einfach in jedem Record als erstes einen Versionsstempel speichern. Bläht zwar die Datei etwas auf, dafür muss am bestehenden Code nicht viel geändert werden.

user profile icondummzeuch hat folgendes geschrieben Zum zitierten Posting springen:
Ein Kollege (Hi DS) hat darauf basierend eine Dateistruktur entwickelt, die sich selbst beschreibt, also den darin gespeicherten Record mit seinen Feldern und diverse andere Angaben, so dass ein Programm die Daten lesen kann, ohne zu wissen, welche Records gespeichert sind.
Hört sich sehr nach XML an, zumindest habe ich das ziemlich genau so wie du es beschreibst damit umgesetzt.
Metschu Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 135

Windows XP SP2 Home
Delphi 7; Delphi XE2-Starter
BeitragVerfasst: So 16.05.10 19:46 
user profile iconjaenicke hat folgendes geschrieben Zum zitierten Posting springen:
Normalerweise gibt es dafür einen Dateikopf, in dem die Informationen zum Format usw. stehen.

Wenn du natürlich aus irgendwelchen Gründen einen file of TXyRecord hast, dann geht das nicht. Dann muss der Benutzer die Umwandlung anstoßen. Würde ich nicht so machen.


Gibt es verschiedene Typen von TRecord?

Ich definiere diese so (Beispiel):
ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
6:
Type TVariable = record
 Variable1 : ShortString;
 Variable2 : Integer;
 Variable3 : Boolean;
 {...}
end;


In der Procedure wird das dann so definiert:

ausblenden Delphi-Quelltext
1:
2:
3:
var V  : TVariable;
   F1 : File of TVariable
{...}


Kann man mit dieser variante den Dateikopf anpassen?
Wenn ja, wonach muß ich suchen?


Danke schonmal.

Gruß

Torsten
jaenicke
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 19326
Erhaltene Danke: 1749

W11 x64 (Chrome, Edge)
Delphi 12 Pro, C# (VS 2022), JS/HTML, Java (NB), PHP, Lazarus
BeitragVerfasst: So 16.05.10 19:53 
Nein, so geht es eben nicht. Deshalb sagte ich ja, dass ich es nicht so mache. ;-)

Ich speichere mehrere Daten in der Datei und als erstes eben den Dateikopf. Mit Records hat das nichts zu tun, ich nutze dafür wie gesagt Klassen.

Und eine weitere Variante mit Nutzung von Records wurde ja auch schon gezeigt. ;-)
dummzeuch
ontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic starofftopic star
Beiträge: 593
Erhaltene Danke: 5


Delphi 5 ent, Delphi 6 bis Delphi XE8 pro
BeitragVerfasst: So 16.05.10 22:17 
user profile iconjaenicke hat folgendes geschrieben Zum zitierten Posting springen:

user profile icondummzeuch hat folgendes geschrieben Zum zitierten Posting springen:
Ein Kollege (Hi DS) hat darauf basierend eine Dateistruktur entwickelt, die sich selbst beschreibt, also den darin gespeicherten Record mit seinen Feldern und diverse andere Angaben, so dass ein Programm die Daten lesen kann, ohne zu wissen, welche Records gespeichert sind.
Hört sich sehr nach XML an, zumindest habe ich das ziemlich genau so wie du es beschreibst damit umgesetzt.


Im Prinzip aehnlich, aber binaer. Das selbstbeschreibende Format ist auch nur als Zusatzfeature gedacht, falls man mal nicht das passende Program zu Hand hat und mal mit einem generischen Betrachter "reinsehen" will. Ausserdem stehen noch ein paar zusaetzliche Angaben im Header, u.a. welches Program die Datei wann und wo erzeugt hat. Normalerweise wird die Datei trotzdem als File of Record mit einem Header variabler Laenge gelesen. Das ist einfach um Klassen schneller.

Wir haben da ein Fremdprogramm im Einsatz, das die Daten als XML erwartet. Da dreht man immer stundenlang Daeumchen, bis das ein paar hundert Datensaetze eingelesen hat....

twm
Hobby-Programmierer
ontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic starofftopic star
Beiträge: 238
Erhaltene Danke: 4

Win XP Pro, Vista Ultimate Trial :o)
D6 Pro, D7 Pro, Turbo, XE SE
BeitragVerfasst: Mo 17.05.10 01:57 
Moin ...,
ich hatte vor Jahren für eine Arbeitszeitdatenbank den Header mittels variable Records am Anfang der Datei geschrieben.
ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
  TIndexRec = Record
    case Daten: TIdx_Data of
     Header:  (HFlag : Boolean;    // negativ wenn Datenbank nicht ordnungsgemäß geschlossen
               HVer  : String[5]; // Version
               HBID  : Cardinal;   // Benutzernr. = Empfängernr
               HCrypt: String[25]);   // Passwort

     Calender:(DTag: TDate;       // Datum
               DVfy: Boolean;     // DS abgeglichen??
               DFt : Byte;        // Feiertag?, wenn 0 = kein oder unbekannt sonst Nr.

               DInx: Byte;        // Dienstschicht, Freizeit, Unterricht, sonstiges )
               DBeg: TTime;       
               DEnd: TTime;      

               L_Azv: Word);       // Link Azv
  end;

Würde jetzt zwar auch Klassen verwenden, aber als Denkanstoss kann mna das ja immer noch gebrauchen.

LG Mario
Reinhard Kern
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 591
Erhaltene Danke: 14



BeitragVerfasst: Mo 17.05.10 14:48 
user profile iconHobby-Programmierer hat folgendes geschrieben Zum zitierten Posting springen:
...Würde jetzt zwar auch Klassen verwenden, aber als Denkanstoss kann mna das ja immer noch gebrauchen.
LG Mario


Hallo,

das eine (Klassen) hat doch mit dem anderen (file of TxxRecord) garnichts zu tun. Zweckmässig wäre es natürlich eine Klasse anzulegen, bei der nach aussen garnicht sichtbar ist, ob sie file of verwendet oder Tabellen oder SQL oder sonstwas.

Gruss Reinhard