Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Object-Pascal / Delphi-Language (https://www.delphipraxis.net/32-object-pascal-delphi-language/)
-   -   Delphi Array als Datei speichern (https://www.delphipraxis.net/14599-array-als-datei-speichern.html)

hummer 12. Jan 2004 12:20


Array als Datei speichern
 
Hallo.
Ich möchte mit einem Programm ein Array als Datei abspeichern und auch wieder öffnen und auslesen. Wie kann ich das realisieren?

maximov 12. Jan 2004 12:33

Re: Array als Datei speichern
 
Hi.

Das kommt auf das array an. Statisches oder dynamisches / ein oder mehrdimensional?

Ich würde auf jeden fall streams verwenden. Wenn dann noch statisch ist, dann kannste es in einem rutsch in die datei streamen:

zb.
Delphi-Quellcode:
var fs:TFileStream;
    arr:array[0..99] od integer;
...
fs:=TFileStream.create('c:\array.dat',fmCreate);
try
  fs.write(arr,sizeOf(arr));
finally
 fs.free;
end;
cu

hummer 12. Jan 2004 12:38

Re: Array als Datei speichern
 
Danke schonmal.
Das Array ist eindimensional und statisch.

Und wie lese ich das Array jetzt wieder aus?

H4ndy 12. Jan 2004 12:45

Re: Array als Datei speichern
 
Das geht relativ einfach über eine TStringList und FOR-Schleifen:

Delphi-Quellcode:
var map: array [1..256] of byte;
...
procedure Thaupt.Save(Sender: TObject);
var StrList: TStrings;
    i: integer;
begin
  if SaveDialog1.Execute then begin
    StrList := NIL;
    try
      StrList := TStringList.Create;
      for i:=1 to 256 do begin
        StrList.Add(IntToStr(map[i]));
      end;
      StrList.SaveToFile(SaveDialog1.FileName);
    finally
      StrList.Free;
    end;
  end;
end;

procedure Thaupt.Open(Sender: TObject);
var StrList: TStrings;
    i: integer;
begin
  if OpenDialog1.Execute then begin
    StrList := NIL;
    try
      StrList := TStringList.Create;
      StrList.LoadFromFile(OpenDialog1.FileName);
      for i:=1 to 256 do begin
        map[i] := StrToInt(StrList[i]);
      end;
    finally
      StrList.Free;
    end;
  end;
Sollte auch einfach auf andere Typen anwendbar sein.

maximov 12. Jan 2004 22:37

Re: Array als Datei speichern
 
@H4ndy: Wers möglichst dirty und nicht binär haben will, der soll es so machen, wie du vorschlägst :mrgreen:


Zitat:

Zitat von hummer
Danke schonmal.
Das Array ist eindimensional und statisch.

Und wie lese ich das Array jetzt wieder aus?

ungefähr so:

Delphi-Quellcode:
var fs:TFileStream;
    arr:array[0..99] od integer;
...

if fileExists('c:\array.dat') then
begin
  fs:=TFileStream.create('c:\array.dat',fmOpenRead);
  try
    if fs.size >= sizeOf(arr)
    then fs.read(arr,sizeOf(arr));
  finally
    fs.free;
  end;
end else showMessage('foobar not da');
cu.

H4ndy 13. Jan 2004 20:14

Re: Array als Datei speichern
 
Zitat:

Zitat von maximov
@H4ndy: Wers möglichst dirty und nicht binär haben will, der soll es so machen, wie du vorschlägst :mrgreen:

Was dagegen :mrgreen:
Quick'n'Dirty ist mein zweiter Vorname :zwinker:

@maximov:
Klappt das mit dem FileStream auch für 2-dimensionale Arrays?

maximov 14. Jan 2004 10:11

Re: Array als Datei speichern
 
Zitat:

Zitat von H4ndy
...
@maximov:
Klappt das mit dem FileStream auch für 2-dimensionale Arrays?

Wenn es statisch ist, ja! Denn dann liegt es in einem stück im speicher.

cu.

hummer 21. Jan 2004 20:30

Re: Array als Datei speichern
 
Kann ich denn auch dynamische Array mit FileStream abspeichern?

Luckie 21. Jan 2004 20:35

Re: Array als Datei speichern
 
Zitat:

Zitat von maximov
Wenn es statisch ist, ja! Denn dann liegt es in einem stück im speicher.

Gilt auch für dynamische. Und aus diesem Grund sollte man sie auch nicht immer nur um ein einzelnes Element vergrößern. Dabei reserviert der Speichermanager von Delphi nämlich jedes mal neuen Speicher für das vergrößerte Array und kopiert das alte, kürzere in den neuen Speicherbereich. Der alte wird dabei aber nicht an Windows zurückgegeben.

maximov 21. Jan 2004 22:11

Re: Array als Datei speichern
 
Da hast du natürlich recht :cyclops:

Ich fake gern dynamische arrays mit statischer deklaration als pointer und um die reservierung kümmere ich mich dann selbst (zb. für DirectX programmierung sehr praktisch)...man muss sich nur die länge der gültigen daten merken.

@Hummer: sollte auch kein prob sein. evtl. musst du einfach auf das erste element referenzieren und dann length()-bytes lesen:

Delphi-Quellcode:
if length(arr)>0 then fs.write(@arr[0]^,length(arr)*sizeOf(Element));
oder so.

Mr_Anderson 7. Okt 2004 19:05

Re: Array als Datei speichern
 
ich habe ein ähnliches Problem. Bei mir ist der Array mit dem Typ Record verknüpft:
Delphi-Quellcode:
type
    zeiten= record
      name: string;
      bzeit: string;
      versuch: integer;
    end;
im Privat Bereich :
Delphi-Quellcode:
testr            : array[1..20] of zeiten;
Danach werden die Werte jeweils durch die Aktionen des Users gesetzt. Danach möchte ich, dass er es speichern und auch wieder laden kann, jedoch funktioniert das nicht mit den oben aufgeführten Möglichkeiten.

Ich habe auch schon Tutorials wie Tut1 durchgelesen, jedoch bringt es mich nicht wirklich weiter.

Ich hoffe ihr könnt mich einen kleinen Tipp geben.

MfG

Keldorn 7. Okt 2004 19:27

Re: Array als Datei speichern
 
Hallo

dann würd ich auch einen Stream nehmen und die Daten mit Hier im Forum suchenTReader und Hier im Forum suchenTwriter lesen und schreiben. Du solltest mit den 2 Suchbegriffen auch Codebsp im forum finden. wenn nicht, meld dich wieder.

Mfg Frank

Luckie 7. Okt 2004 19:29

Re: Array als Datei speichern
 
Nimm mal ShortStrings in deinem Record.

Mr_Anderson 7. Okt 2004 22:39

Re: Array als Datei speichern
 
Zitat:

Zitat von Keldorn
Hallo

dann würd ich auch einen Stream nehmen und die Daten mit Hier im Forum suchenTReader und Hier im Forum suchenTwriter lesen und schreiben. Du solltest mit den 2 Suchbegriffen auch Codebsp im forum finden. wenn nicht, meld dich wieder.

Mfg Frank

Ich habe die 14 Ergebnisse abgesucht und bin doch nicht schlauer geworden. :( den einzigen code, den ich gefunden habe ist der hier:
Delphi-Quellcode:
procedure WriteStringToStream(stream: TFileStream; str: string);
var
len: integer;
begin
len := length(str);            // Länge des strings ermitteln
stream.write(len, SizeOf(len)); // Länge des Strings speichern
stream.write(PChar(str)^, len); // String mittels PChar speichern
end;


procedure ReadStringFromStream(stream: TFileStream; var str: string);
var
len: integer;
begin
stream.read(len, SizeOf(len)); // Länge des Strings auslesen
SetLength(str, len);          // Länge des Srings setzen
stream.read(PChar(str)^, len); // Strings mittels PChar auslesen
end;
Jedoch speichert dieser code nur 'normale' record und keinen, der mit einem Array in verbindung steht. Außerdem wird hier auch kein TWriter oder TReader benutzt, was ich ja gerade benutzten sollte, wegen der Strukturen. Auch die Delphi Hilfe schweigt bei der eingabe von TWriter....
--> nur unter der Wortsuche fand ich einen kleinen Artikel der jediglich die eigenschaften beschrieb, jedoch nicht auf die Syntaxs einging.



MfG

Luckie 7. Okt 2004 23:02

Re: Array als Datei speichern
 
So geht es:
Delphi-Quellcode:
// unser Record
type
  TScoreRecord = packed record
    Player: string[25];
    Score: Cardinal;
  end;

// unser Array vom Typ unseres Records
var
  ScoreRecord: array[0..2] of TScoreRecord;

procedure TForm1.Button1Click(Sender: TObject);
var
  fs: TFileStream;
begin
  // Array fülllen
  ScoreRecord[0].Player := 'Hans';
  ScoreRecord[0].Score := 5;
  ScoreRecord[1].Player := 'Fritz';
  ScoreRecord[1].Score := 6;
  ScoreRecord[2].Player := 'Lieschen';
  ScoreRecord[2].Score := 4;
  // abspeichern
  fs := TFileStream.Create('d:\HighScore.hs', fmCreate);
  try
    try
      // Array in Datei schreiben
      fs.WriteBuffer(ScoreRecord, sizeof(ScoreRecord));
    except
      // Schreibfehler aufgetreten
      on E: EWriteError do
        ShowMessage(E.Message);
    end;
  finally
    // fs wieder freigeben
    FreeAndNil(fs);
  end;
end;

procedure TForm1.Button2Click(Sender: TObject);
var
  fs: TFileStream;
  i: Integer;
begin
  fs := TFileStream.Create('d:\HighScore.hs', fmOpenRead);
  try
    try
      // erstmal wieder leeren
      for i := 0 to length(ScoreRecord) - 1 do
      begin
        ScoreRecord[i].Player := '';
        ScoreRecord[i].Score := 0;
      end;
      // aus Datei einlesen
      fs.ReadBuffer(ScoreRecord, sizeof(ScoreRecord));
      // und kucken was drinne steht
      for i := 0 to length(ScoreRecord) - 1 do
      begin
        ShowMessage(ScoreRecord[i].Player+#13#10+IntToStr(ScoreRecord[i].Score));
      end;
    except
      // Lesefehler aufgetreten
      on E: EReadError do
        ShowMessage(E.Message);
    end;
  finally
    // fs wieder freigeben
    FreeAndNil(fs);
  end;
end;

maximov 8. Okt 2004 09:34

Re: Array als Datei speichern
 
Zitat:

Zitat von Keldorn
Hallo

dann würd ich auch einen Stream nehmen und die Daten mit Hier im Forum suchenTReader und Hier im Forum suchenTwriter lesen und schreiben. Du solltest mit den 2 Suchbegriffen auch Codebsp im forum finden. wenn nicht, meld dich wieder.

Mfg Frank

Das ist ein sehr guter rat! Damit lassen sich dynamische strukturen viel transparenter speichern.

Tu dir selbst einen gefallen und fang garnicht erst an, records direkt binär zu speichern, erst recht nicht wenn da shortStrings drinn sind (platzverschwendung). Wenn du zB. die lange des strings, oder daten hinzufügen willst, dann ist das format deiner alten daten inkompatibel.

Ich mach es oft so, das ich erst eine signatur, versions-nummer und ein schema speichere, das besagt welche daten geschrieben werden. Beim auslesen weiss man dann genau, was man wohin laden muss.

Mr_Anderson 8. Okt 2004 14:11

Re: Array als Datei speichern
 
Zitat:

Zitat von maximov
Zitat:

Zitat von Keldorn
Hallo

dann würd ich auch einen Stream nehmen und die Daten mit Hier im Forum suchenTReader und Hier im Forum suchenTwriter lesen und schreiben. Du solltest mit den 2 Suchbegriffen auch Codebsp im forum finden. wenn nicht, meld dich wieder.

Mfg Frank

Das ist ein sehr guter rat! Damit lassen sich dynamische strukturen viel transparenter speichern.

Tu dir selbst einen gefallen und fang garnicht erst an, records direkt binär zu speichern, erst recht nicht wenn da shortStrings drinn sind (platzverschwendung). Wenn du zB. die lange des strings, oder daten hinzufügen willst, dann ist das format deiner alten daten inkompatibel.

Ich mach es oft so, das ich erst eine signatur, versions-nummer und ein schema speichere, das besagt welche daten geschrieben werden. Beim auslesen weiss man dann genau, was man wohin laden muss.

So langsam habe ich es ja verstanden, jedoch fehlt mir ein konkretes Beispiel mit TReader und TWriter. Das einzige, was ich in der CL und im Forum finde, beschreibt niemals die genaue Verwendung der Syntaxs.

habt ihr vieleicht einen Konkreten link etc...
MfG

@Luckie thx, werde ich sofort mal testen

Luckie 8. Okt 2004 14:16

Re: Array als Datei speichern
 
Zitat:

Zitat von Mr_Anderson
@Luckie thx, werde ich sofort mal testen

Da gfibt es nichts zum testen, das läuft. ;)

Mr_Anderson 8. Okt 2004 14:42

Re: Array als Datei speichern
 
nunja funktionieren stelle ich mir anders vor :P

Also es funktioniert nur einmal. Bei einem erneuten ausführen der laden funktion stüzt delphi komplett ab :D, wegen einer "Zugriffsverletzung".
Außerdem zeigt er mir nicht die Werte an, er zeigt nichts an im "showmessage":

Code sieht so aus:
Delphi-Quellcode:
type
    zeiten= packed record
      name: string;
      bzeit: string;
      versuch: integer;
    end;
var testr            : array[1..20] of zeiten;

procedure TForm1.SpeedButton3Click(Sender: TObject);
var
    fs: TFileStream;
  i: Integer;
begin

  fs := TFileStream.Create('d:\HighScore.hs', fmOpenRead);
  try
    try
      // erstmal wieder leeren
      for i := 0 to length(testr) - 1 do
      begin
        testr[i].bzeit := '';
        testr[i].name := '';
        testr[i].versuch := 0;
      end;
      // aus Datei einlesen
      fs.ReadBuffer(testr, sizeof(testr));
      // und kucken was drinne steht
      for i := 0 to length(testr) - 1 do
      begin
        ShowMessage('Bzeit:'+testr[i].bzeit+#13#10+'Verusch:'+IntToStr(testr[i].versuch)+#13#10+'name'+testr[i].name);
      end;
    except
      // Lesefehler aufgetreten
      on E: EReadError do
        ShowMessage(E.Message);
    end;
  finally
    // fs wieder freigeben
    FreeAndNil(fs);
  end;
end;
procedure TForm1.SpeedButton2Click(Sender: TObject);
var outputfile : textfile;
    i : word;
    StrList: TStrings;
    y: integer;
    fs: TFileStream;
begin
  testr[1].name := 'muh';
  testr[2].name := 'jta';
  fs := TFileStream.Create('d:\HighScore.hs', fmCreate);
  try
    try
      // Array in Datei schreiben
      fs.WriteBuffer(testr, sizeof(testr));
    except
      // Schreibfehler aufgetreten
      on E: EWriteError do
        ShowMessage(E.Message);
    end;
  finally
    // fs wieder freigeben
    FreeAndNil(fs);
  end;
end;

Luckie 8. Okt 2004 14:47

Re: Array als Datei speichern
 
Liste der Anhänge anzeigen (Anzahl: 1)
Bei mir definitiv nicht, sonst hätte ich das hier nicht gepostet.

Demo im Anhang.

Mr_Anderson 8. Okt 2004 14:54

Re: Array als Datei speichern
 
Ich frage mich, warum er bei dir nicht abstürzt und bei mir schon :/ grml. Ich hbae den code nur angepasst, nicht mehr.

code : siehe editieren Beitrag

MfG

//edit:
wenn ich bei mir erst "lesen" lasse und dann auf speichern klicke stürzt das Programm ebenfalls mit einer Zugriffsverletzung ab.

maximov 8. Okt 2004 15:55

Re: Array als Datei speichern
 
Du benutzt nach wie vor normale strings in deinem record, und nicht string[?].

Zu TReader und TWriter (Filer) hab ich hier mal was geschrieben: http://www.delphipraxis.net/internal...ct.php?t=31873 das hilft vielleicht

Mr_Anderson 8. Okt 2004 16:13

Re: Array als Datei speichern
 
Zitat:

Zitat von maximov
Du benutzt nach wie vor normale strings in deinem record, und nicht string[?].

Aehm string[?] ?
Vielleicht ist es für dich trivial, jedoch vermag ich es nicht zu verstehen.

MfG

Luckie 8. Okt 2004 19:14

Re: Array als Datei speichern
 
Du musst ShortStrings benutzen, also entwerde
Delphi-Quellcode:
s: ShortString;
oder mit direkter Längenangabe:
Delphi-Quellcode:
s: String[55];

Robert_G 8. Okt 2004 20:38

Re: Array als Datei speichern
 
@maximov
Hattest du nicht zusammen mit Jens einen TCollection descendant gebastelt?
Ich hatte ihn nie ausprobiert, aber mit Interesse euren Fortschritt verfolgt.
Ich persönlich ziehe Objekte dem Hantieren mit primitiven Typen vor, mit dem TCollection-Abkömmling bekommt man anscheinend eine hübsche Möglichkeit zur Objektpersistenz. ;)

Nachtrag: Hab's gefunden... TmxJsCollectionTmxJsCollection

Mr_Anderson 8. Okt 2004 22:06

Re: Array als Datei speichern
 
Delphi-Quellcode:
procedure TForm1.SpeedButton3Click(Sender: TObject);
var
  fs: TFileStream;
  m: Integer;
  begin
   if opendialog1.execute then begin
     fs := TFileStream.Create(OpenDialog1.FileName, fmOpenRead);{ExtractFilePath(ParamStr(0))+'HighScore.hs'}
     try
        try
          // erstmal wieder leeren
          for m := 0 to length(testr) - 1 do
            begin
              testr[m].bzeit := '';
              testr[m].name := '';
              testr[m].versuch := 0;
          end;
        // aus Datei einlesen
        fs.ReadBuffer(testr, sizeof(testr));
        except
        // Lesefehler aufgetreten
          on E: EReadError do
          ShowMessage(E.Message);
        end;
     finally
      // fs wieder freigeben
      FreeAndNil(fs);
    end;
  end;
end;
Beim zweiten Mal stürzt das Programm ebenfalls ab. Angeblich wieder wegen einer Zugriffsverletzung ( Habe strings auf 10 Zeichen begrenzt!)
Wenn ich es ohne OpenDialog mache klappt es wunderbar. Dafür schon mal ein großes Dankeschön :)

MfG

Luckie 8. Okt 2004 22:47

Re: Array als Datei speichern
 
Dann geh mal mit F7 durch den Quellcode durch.

Dani 9. Okt 2004 00:32

Re: Array als Datei speichern
 
Mit TReader und TWriter habe ich nur schlechte Erfahrungen gemacht, z.B. war die Größe des Filestreams nach den ersten Aufruf von ReadString() plötzlich Null. Für einfache Datentypen wie Integer, String und Double hab ich mir deshalb einfache, eigene TFiler-Klassen zusammengeschustert... die sind aber so billig, dass ich mich nicht traue, die jetzt auch noch hier reinzuposten *g*

PS: Es ist zwar Geschmackssache, aber anstelle eines Arrays würde ich eher zu TList und anstelle von Records eher zu class(TObject) tendieren

Keldorn 9. Okt 2004 07:55

Re: Array als Datei speichern
 
@Mr_Anderson
Delphi-Quellcode:
var testr            : array[[color=red]1[/color]..20] of zeiten;
Delphi-Quellcode:
          // erstmal wieder leeren
          for m := [color=red]0[/color] to length(testr) - 1 do
            begin
              testr[m].bzeit := '';
              testr[m].name := '';
              testr[m].versuch := 0;
          end;
du definierst dein array von 1.. und greifst aber auf 0. Element zu. über eine Zugriffsverletzung brauchst du dich dann nicht zu wundern ;)

Zitat:

Zitat von Dani
Mit TReader und TWriter habe ich nur schlechte Erfahrungen gemacht, z.B. war die Größe des Filestreams nach den ersten Aufruf von ReadString() plötzlich Null.

ich nicht ;). ich machs geanu wie maximov, meine objekte besitzen methoden saveto- und loadfromstream. Auch mit der versionsnummer, du kannst so sehr schnell auf Änderungen beim Abspeichern reagieren, und hast so ein flexibles darteiformat.

Mfg Frank

Mr_Anderson 9. Okt 2004 11:13

Re: Array als Datei speichern
 
Hi
Warum eigentlich immer so ganz kleine Fehler mir passieren :) :wall: :wall: Herzlichen Dank an Keldorn. Jetzt funktioniert es. Was mich jetzt dennoch interessiert, was du damit genau meinst
Zitat:

ich nicht Wink. ich machs geanu wie maximov, meine objekte besitzen methoden saveto- und loadfromstream. Auch mit der versionsnummer, du kannst so sehr schnell auf Änderungen beim Abspeichern reagieren, und hast so ein flexibles darteiformat.
Ich hoffe ich stelle nicht zu triviale Fragen, jedoch haben wir gerade mit Delphi angefangen :/ vorher haben wir Krimskram gemacht ( CSS, HTML, XML, JS, Javascript, Java ) und dort so vieles anders :) :warn: :angel:

MfG

Keldorn 9. Okt 2004 11:52

Re: Array als Datei speichern
 
wenn du daten aus einem stream lädst, mußt du sie genausso einlesen, wie du sie reingeschrieben hast. Wenn sich dein Record mal erweitert, weil du der Meiniung bist, das du noch ein weiteres Feld benötigst, dann hast du ein Problem. Deine vorher gespeicherten Daten passen nun nicht mehr und du müßtest dir ein konvertierungsprogramm schreiben, das dir die alten Daten auf das neue format "anpaßt", gerade wenn du in der Entwicklung bist und du noch nicht richtig weißt, wie dein Record/Object aufgebaut ist, kostet dich das unnötig viel Zeit und kann schnell Fehler verursachen.

Es gibt da sicher noch andere Lösungsmöglichkeiten, ich hab mich dafür entschieden und bin recht glücklich damit. die "falschen" Einrückungen sind Absicht:

Delphi-Quellcode:
//Versionskonstanten fürs Speichern/Laden der Daten
const Version1  = 1;
      Version2  = 2;
      Version3  = 3;
      Version4  = 4;

      Version_neu = 4;

procedure TSendung.LoadFromStream(stream: TStream);
var Version:integer;
  begin
    with TReader.create(Stream,1024) do
    try
      begin
        //Version auslesen
        Version:=ReadInteger;

        Auftrag       := readstring;
        Kunde         := readstring;
        Kundennummer  := readstring;
        PLZ           := readstring;
        Ort           := readstring;
        Land          := readstring;
        VA_Typ        := ReadInteger;
        if version>=Version2 then
          begin
        Bestelldaten  := readstring;
        AuftragsArt   := readstring;
        Baunummer     := readstring;
          end;
        if version>=Version3 then
          begin
        KdNrVertretung  := readstring;
          end;
        //hier hinzufügen
      end;
    finally
      free;
    end;

end;

procedure TSendung.SaveToStream(stream: TStream);
//Daten in Stream Speichern
  begin
    with Twriter.create(Stream,1024) do
    try
      begin
        //version schreiben
        writeinteger(Version_neu);
        //Daten schreiben
        writestring(Auftrag);
        writestring(Kunde);
        writestring(Kundennummer);
        writestring(PLZ);
        writestring(Ort);
        writestring(Land);
        writeinteger(VA_Typ);
        WriteString(Bestelldaten);
        writestring(AuftragsArt);
        writestring(Baunummer);
        writestring(KdNrVertretung);
        //hier hinzufügen
      end;
    finally
      free;
    end;
  end;


Alle Zeitangaben in WEZ +1. Es ist jetzt 04:12 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