Autor Beitrag
Master_of_Magic
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 56

Win 98, Win XP
D6 Pers, D2005 Arch
BeitragVerfasst: So 16.10.05 12:49 
Ich habe ein Tool für ein Spiel geschrieben, mit dem man seine Planeten verwalten kann. Dabei werden alle Planetenbezogenen Daten in einer .ini gespeichert. Da ich das Programm später für mehrere Planis erweitert habe, muss nun bei jeden Planetenwechsel die alten Daten gespeichert und die neuen geladen werden (aus der .ini).
Wie ihr euch denken könnt, ist das nervig und umständlich.

Damit ich immer Zugriff auf alle Daten habe, möchte ich diese ganzen Daten nun mit Arrays im Speicher halten und dann auch gesammelt speichern/laden.

Vom Aufbau her soll es folgendermaßen funktionieren:

1. Man hat mehrere Accounts
2. jeder Account enthält einige Daten in Array-Form
3. jeder Account hat bis zu 13 Planeten
4. jeder Planet hat wiederum etliche Daten in Array-Form

Da die Arrays unterschiedlich lang sind, bieten sich Records an. Da ich aber bisher weder mit mehrdimensionalen Arrays, noch mit Records gearbeitet habe, möchte ich wissen, ob meine Lösung so in Ordnung ist!

Da alle Angaben/Arrays eine feste Länge haben (zumindest kann ich ein Limit setzen) ist das Ganze also statisch.

Meine Deklaration sieht momentan folgendermaßen aus:

ausblenden 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:
type
  TPlaneten = Record
    Name:string[30];
    Gebäudelevel: array[0..15] of integer;
    Stempelbestand: array[0..2] of int64;
    Stempeldatum: string[10]; //Länge 10 (Datum)
    Stempelzeit: string[8]; //Länge 8 (Uhrzeit)
    Benutzerprojekte: array[0..11] of integer; //evtl. auch Int64
    Schiffsanzahl: array[0..17] of integer;
    Verteidigungsanzahl: array[0..9] of integer;
    Wirtschaftseinstellungen: array[0..5] of integer; //evtl. auch float
    Gebäudebenachrichtigungen: array[0..13] of boolean;
    Schiffsbenachrichtigungen: array[0..18] of boolean;
    Verteidigungsbenachrichtigungen: array[0..10] of boolean;
  end;

  TAccount = Record
    Planet: array[0..12] of TPlaneten;
    Forschungslevel: array[0..19] of integer;
    Forschungsermäßigungen: array[0..19] of integer;
    Forschungsbenachrichtigungen: array[0..20] of boolean;
    Urlaubsrechnerdaten: array[0..70] of int64;
    Urlaubsrechnerzeiten: array[0..9] of string[19]; //Länge 19 (Datum+Uhrzeit)
  end;

//[...]

var
  Account: array[0..7] of TAccount;


Ich denke, dass müsste soweit in Ordnung sein oder seht ihr noch ein Problem?

Wenn ich jetzt z.B. das fünfte Gebäudelevel meines elften Planeten im zweiten Account auf 16 setzen möchte, muss ich dass dann mit

ausblenden Delphi-Quelltext
1:
Account[1].Planet[10].Gebäudelevel[4] := 16;					


machen oder gibts da eine Kurzform?


Und jetzt zum speichern:
Vorher hatte ich wie gesagt eine .ini. Da konnte ich mit

ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
procedure TForm1.savedat(index:integer);
var save: TInifile;
i: integer;
begin
  try
    save := TIniFile.Create(GetCurrentDir + '\sessiondata.ini');
    save.WriteBool('Settings','chkontop',MainMenu.Items[0][1].Checked);
    //[...] gekürzt
    save.WriteString('Textdata'+inttostr(index),'edtmet',edtmet.text);
    for i:=1 to 16 do save.WriteString('Textdata'+inttostr(index),'gs' + inttostr(i),(FindComponent('gs' + inttostr(i)) as TEdit).Text);
    for i:=1 to 20 do save.WriteString('Textdata'+inttostr(index),'fs' + inttostr(i),(FindComponent('fs' + inttostr(i)) as TEdit).Text);
    //[...] gekürzt
    save.Writestring('Otherdata'+inttostr(index),'selplan',selplan.Items[index]);
    save.Writedate('Otherdata'+inttostr(index),'setdate',setdate.date);
    save.Writetime('Otherdata'+inttostr(index),'settime',settime.Time);
    save.WriteInteger('Otherdata'+inttostr(index),'mmprod',mmprod.ItemIndex);
    //[...] gekürzt
    // die Liste ist noch länger, aber hier gehts ums Prinzip
 finally
    save.Free;
  end;
end;


speichern und dann mit

ausblenden 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:
procedure TForm1.loaddat(index:integer);
var load: TiniFile;
    keys: Tstrings;
    i: integer;
begin
  if fileexists('sessiondata.ini') then
  begin
    keys := Tstringlist.Create;
    try
      load := TIniFile.Create(GetCurrentDir + '\sessiondata.ini');
      load.ReadSection('Textdata'+inttostr(index),keys);
      for i:=0 to keys.Count-1 do (FindComponent(keys[i]) as TCustomEdit).Text := load.ReadString('Textdata'+inttostr(index),keys[i],'0');
      for i:=0 to selplan.Items.Count-1 do selplan.Items[i]:=load.ReadString('Otherdata'+inttostr(i),'selplan','Planet ' + inttostr(i+1));
      setdate.Date := load.ReadDate('Otherdata'+inttostr(index),'setdate',now);
      settime.time := load.ReadTime('Otherdata'+inttostr(index),'settime',now);
      mmprod.ItemIndex := load.ReadInteger('Otherdata'+inttostr(index),'mmprod',0);
      //[...] usw.
    finally
      load.Free;
    end;
  end
  else
  begin
    setdate.DateTime:=now;
    settime.time:=now;
    for i:=1 to 10 do (FindComponent('uitime'+inttostr(i)) as TMaskEdit).Text:=DateTimetostr(now);
    MainMenu.Items[0][0].Checked:=false;
  end;
  selplan.ItemIndex:=index;
end;


ganz einfach laden. Vorallem die Textdata-Sektion konnte ich so mit einer Schleife auslesen, da die Keynamen den Komponentennamen entsprachen.


Aber jetzt wäre eine .ini Lösung wohl arg umständlich. Ich habe mir überlegt einige alte Infos (Programmeinstellungen) in der .ini zu lassen und mein neues Account-Array anders zu speichern.
Ein bischen Suchen und rumprobieren hat mir dann folgende Lösung gebracht:

ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
procedure TForm1.Button3Click(Sender: TObject);
var
f:file of TAccount;
i:integer;
begin
assignfile(f,'Daten.dat');
rewrite(f); //heißt das, die Datei wird immer überschrieben, anstatt die Daten dranzuhängen?
for i:=0 to 7 do
  write(f,Account[i]);
CloseFile(f);
end;


Jetzt hab ich noch ein kleines Problem: Wie lese ich diese Datei wieder genau so in mein Array ein? Oder kennt ihr einen besseren Weg, die Sachen zu speichern?

Und kann ich auch nur Teile meines Array speichern? Weil Programmintern werden die Werte zu weiteren Berechnungen genutzt, die aber nicht mit abgespeichert werden sollen! Oder soll ich dazu einfach ein zweites Array machen, dass ich nicht abspeichere?

Ich hoffe, das waren nicht zu viele Fragen auf einmal :wink:


Zuletzt bearbeitet von Master_of_Magic am So 06.11.05 11:42, insgesamt 1-mal bearbeitet
alzaimar
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 2889
Erhaltene Danke: 13

W2000, XP
D6E, BDS2006A, DevExpress
BeitragVerfasst: So 16.10.05 13:19 
1. normalerweise eine Frage pro Thread.
2. File Of TMyRecord ist legitim, aber veraltet.
3. Rewrite löscht die Datei (bzw setzt die Länge auf 0).
4. Rauslesen genauso wie reinschreiben, ausser, das die Anzahl der Records nicht bekannt ist. Also liest man bis zum ende der Datei ('EOF')

Zu 2: Statt File Of xxx nimmt man heutzutage ein TFileStream.

_________________
Na denn, dann. Bis dann, denn.
Master_of_Magic Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 56

Win 98, Win XP
D6 Pers, D2005 Arch
BeitragVerfasst: So 16.10.05 14:23 
zu 1. da alle Fragen zum selben Thema gehören, fänd ich es blödsinnig, für jede einen extra Thread aufzumachen.

zu 2. wo ist das Problem an TMyRecord bzw. was ist an TFileStream besser? Mit letzterem hab ich noch nie gearbeitet und daher auch keinen Schimmer davon ...

zu 3. wenn ich das also weglasse, müsste die Datei jedesmal größer werden... dann passt das so :-)

zu 4. Auslesen hab ich glaube ich hinbekommen, zumindest bringt mir

ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
procedure TForm1.Button5Click(Sender: TObject);
var
f:file of TAccount;
i:integer;
begin
assignfile(f,'bla.bla');
reset(f);
for i:=0 to 7 do
  Read(f,Account[i]);
CloseFile(f);
end;


keinen Fehler und scheint zu funktionieren. Aber die Anzahl der Records ist doch bekannt, warum sollte ich da bis EOF lesen müssen?
Amateur
ontopic starontopic starontopic starontopic starontopic starhalf ontopic starofftopic starofftopic star
Beiträge: 777

(Win98, WinMe) WinXP Prof
D3 Prof, D6 Pers, D2k5 Pers., Turbo C++ Explorer
BeitragVerfasst: So 16.10.05 14:32 
zu 3. es gibt nen anderen befehl der bewirkt dass am ende der datei weitergeschrieben wird. aber ich komm im moment nicht drauf wie der heißt...

_________________
"Kein dummes Gerede. Kein Rumrätseln. Denkt an nichts anderes mehr, nur noch an das, was vor euch liegt. Das ist die wahre Herausforderung. Ihr müßt euch vor euch selbst schützen, Leute." (Rennes in "Cube")
Beiträge: >700
alzaimar
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 2889
Erhaltene Danke: 13

W2000, XP
D6E, BDS2006A, DevExpress
BeitragVerfasst: So 16.10.05 14:42 
ausblenden Delphi-Quelltext
1:
Append					

_________________
Na denn, dann. Bis dann, denn.
ub60
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 765
Erhaltene Danke: 130



BeitragVerfasst: So 16.10.05 15:20 
Append funzt meiner Meinung nach nur bei Textfiles, nicht bei typisierten Files.
Die Anzahl der Datensätze bekommst Du mit FileSize.
An diese Position (nach dem letzten DS) kannst Du dann den Datensatzzeiger mit Seek setzen.
Dann kann man mit Write weiterschreiben.
Etwa so:

ausblenden Delphi-Quelltext
1:
2:
3:
Anzahl:=FileSize(Datei);
Seek(Datei,Anzahl);
Write(Datei,Datensatz);


ub60
Master_of_Magic Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 56

Win 98, Win XP
D6 Pers, D2005 Arch
BeitragVerfasst: So 16.10.05 15:35 
Wie gesagt, ich möchte ja, dass die Datei aktualisiert (überschrieben) wird. Daher ist Rewrite ja ok, ich wusste nur anfangs nicht wofür es da ist ...

Mir gehts eher darum, ob meine Vorgehensweise richtig ist und ich nicht nachher alles neu schreiben muss, weil das System so nicht funktioniert ... ;-)
Amateur
ontopic starontopic starontopic starontopic starontopic starhalf ontopic starofftopic starofftopic star
Beiträge: 777

(Win98, WinMe) WinXP Prof
D3 Prof, D6 Pers, D2k5 Pers., Turbo C++ Explorer
BeitragVerfasst: So 16.10.05 15:51 
was ist mit der kurzform von

Account[1].Planet[10].Gebäudelevel[4] := 16;

geht das auch anders? ich glaub nur mit einer for schleife oder? allerdings wird dann die werteverteilung schwer... müsste mit ner formel oder so berechnet werden...

_________________
"Kein dummes Gerede. Kein Rumrätseln. Denkt an nichts anderes mehr, nur noch an das, was vor euch liegt. Das ist die wahre Herausforderung. Ihr müßt euch vor euch selbst schützen, Leute." (Rennes in "Cube")
Beiträge: >700
Master_of_Magic Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 56

Win 98, Win XP
D6 Pers, D2005 Arch
BeitragVerfasst: So 06.11.05 10:08 
Ok, mit dem Speichern bin ich soweit klar gekommen. Muss nur noch entscheiden, ob ich File of ... lassen soll oder ein Filestream mache (macht das hier einen Unterschied?).

Jetzt hab ich aber eine Kompatibilitätsfrage: Wenn ich mein Programm mal erweitere und z.B. ein Array in meinem Record hinzukommt, gibt es dann eine Möglichkeit, eine alte Datei in den Record einzulesen und dass neue Array dann einfach leer zu lassen? Weil bisher kommt ja logischerweise ein 'Read beyond end of File'-Error ...