Dateiformat (erstellen, speichern, laden)

Für alles, was in den übrigen Lazarusthemen keinen Platz, aber mit Lazarus zutun hat.
bembulak
Beiträge: 370
Registriert: Di 6. Feb 2007, 09:29
OS, Lazarus, FPC: L0.9.29 SVN:24607 FPC 2.4.0-32 bit @ Win XP SP3
CPU-Target: 32bit i386, ARM
Wohnort: Oberösterreich

Dateiformat (erstellen, speichern, laden)

Beitrag von bembulak »

Hallo!

Gerade mache ich mir Gedanken, wie Dateiformate unter Lazarus wohl aussehen könnten, die man selber definiert und wie man diese dann speichern und auch wieder laden kann.

Meine Gedanken gingen in Richtung Records, aber ich bin nicht sicher ob es die richtige Wahl ist. Ich möchte kein Format, das "variabel" ist, wie z.B. Word-Dokumente oder so, sondern ein fest definiertes Format.

Ich hätte es mir ungefähr so vorgestellt:

Code: Alles auswählen

type
  TProjekt = Record
    projektName : string[32];
    projektNummer  : string[20];
    Startdatum  : string[12];
    Enddatum  : string[12];
    Betrag: Real;
  end;
 
var
  projekt1 : TCustomer;
 [...]
begin
 with projekt1 do
 begin
    projektName := Edit1.Text;
    [...]
 end;
Wenn ich das gefundene Record-Beispiel richtig interpretiert habe, so kann ich ja durch Variable : string[zahl]; festlegen, wieviele Zeichen der String maximal aufnehmen soll. Wenn ich dieses Record dann speichern könnte, könnte ich sicher stellen, dass die Datei immer die gleiche Größe hat und muss mich nicht darum kümmern, was und wieviel ich auslesen muss.

So, jetzt kommt der Teil, denn ich noch überhaupt nicht weiß und deshalb muss ich konkret Fragen:
kann ich das Record "einfach" in eine Dateischreiben? Ich stelle mir was vor, wie etwa

Code: Alles auswählen

Write(dateihandle, name_vom_Record, SizeOf(Typ_vom_Record))
 
{ und zum Lesen }
 
Read(dateihandle, name_vom_record, SizeOf(Typ_vom_Record))
Dann müsste ich genauso, wie ich oben in das Record Daten geschrieben habe, dies auch wieder haben, wenn ich die Datei auslese, oder?

Für sowas (oder was ähnliches) gibt es doch sicher Befehle?

Wäre nett, wenn mir jemand das Prinzip ein wenig erklären könnte. Danke!

hanibal
Beiträge: 369
Registriert: Sa 3. Mär 2007, 16:03
OS, Lazarus, FPC: Winux (L 0.9.xy FPC 2.2.z)
CPU-Target: xxBit
Wohnort: Bramsche (Emsland)

Beitrag von hanibal »

das hat glaub ich was mit fileof zu tun, ich muss da gleich mal ebend nachgucken, ich hab da ein beispiel code irgendwo rumliegen

Benutzeravatar
theo
Beiträge: 10927
Registriert: Mo 11. Sep 2006, 19:01

Beitrag von theo »

Ich finde sowas geht am einfachsten mit TStream.
z.B. TFileStream.
http://www.freepascal.org/docs-html/rtl ... tream.html" onclick="window.open(this.href);return false;

Christian
Beiträge: 6079
Registriert: Do 21. Sep 2006, 07:51
OS, Lazarus, FPC: iWinux (L 1.x.xy FPC 2.y.z)
CPU-Target: AVR,ARM,x86(-64)
Wohnort: Dessau
Kontaktdaten:

Beitrag von Christian »

Code: Alles auswählen

type
  TProjekt = Record
    projektName : string[32];
    projektNummer  : string[20];
    Startdatum  : TDateTime;
    Enddatum  : TDateTime;
    Betrag: Real;
  end;
 
var
  MyProject : TRecord;
  f : file of TProject;
 [...]
begin
  AssignFile(f,'test.dat');
  Rewrite(f);
  MyProject.ProjectName := 'Testproject';
  MyProject.StartDatum := Now();
  write(f,MyProject);
  CloseFile(f);
 
  MyProject.ProjectName := 'mal sehn was da gleich steht';
 
  Reset(f);
  read(f,Myproject);
  Closefile(f);
 
  writeln(Myproject.projectname);
end;
Mit Seek(f,Datensatznummer) kannst du zu einem bestimmten Datensatz springen.

Textdateien sind z.b.

type
TextFile = File of char;

klar ?
W.m.k.A.h.e.m.F.h. -> http://www.gidf.de/

Benutzeravatar
theo
Beiträge: 10927
Registriert: Mo 11. Sep 2006, 19:01

Beitrag von theo »

@Christian: Worin siehst du den Vorteil gegenüber TFileStream?
Ich frage mich schon länger, wozu man die alten Pascal Datei-Zugriffsmethoden überhaupt noch braucht.

Christian
Beiträge: 6079
Registriert: Do 21. Sep 2006, 07:51
OS, Lazarus, FPC: iWinux (L 1.x.xy FPC 2.y.z)
CPU-Target: AVR,ARM,x86(-64)
Wohnort: Dessau
Kontaktdaten:

Beitrag von Christian »

1. Es ist einfach
2. TFileStream ist um längen langsamer.
3. Es war die Fragestellung.

TReader/TWriter sind sehr schön zum speichern aber bringen auch nur dann Vorteile, wenn man eine Klassenstruktur benutzt oder mehrere verschiedene Klassentypen in diesem fall z.b. TProject, TCustomer, TTask hat.
W.m.k.A.h.e.m.F.h. -> http://www.gidf.de/

Benutzeravatar
theo
Beiträge: 10927
Registriert: Mo 11. Sep 2006, 19:01

Beitrag von theo »

Christian hat geschrieben:1. Es ist einfach
Ich finde TFileStream einfacher
Christian hat geschrieben:2. TFileStream ist um längen langsamer.
Wieso? Hast du das gemessen?

Christian
Beiträge: 6079
Registriert: Do 21. Sep 2006, 07:51
OS, Lazarus, FPC: iWinux (L 1.x.xy FPC 2.y.z)
CPU-Target: AVR,ARM,x86(-64)
Wohnort: Dessau
Kontaktdaten:

Beitrag von Christian »

Ich finde TFileStream einfacher
Dann täuscht dich dein empfinden, mit TFilestream musst du beim lesen/schreiben immer die grösse des zu lesenden Blockes mitübergeben und beim Suchen innerhalb der Datei musst du auch den Offset ded Datensatzes ausrechnen. Da fällt einiges an Fehlerträchtigkeit weg bei Records.
Wieso? Hast du das gemessen?
Ich nicht aber ich hab mal Messungen dazu gelesen da war FileStream knapp um das 10 Fache langsamer. Ich weiss aber leider nicht mehr genau wo das war war eine private Homepage wo jemand drüber gestolpert ist das es so sein könnte und dann genaue Messungen angestellt hatte.



Ich nicht,
W.m.k.A.h.e.m.F.h. -> http://www.gidf.de/

Euklid
Lazarusforum e. V.
Beiträge: 2808
Registriert: Fr 22. Sep 2006, 10:38
OS, Lazarus, FPC: Lazarus v2.0.10, FPC 3.2.0
Wohnort: Hessen
Kontaktdaten:

Re: Dateiformat (erstellen, speichern, laden)

Beitrag von Euklid »

bembulak hat geschrieben: Wäre nett, wenn mir jemand das Prinzip ein wenig erklären könnte. Danke!
Hallo Bembulak!

Möchte zwar jetzt keine Werbung machen *hust*, aber hier ein Ausschnitt aus Promathika, in denen das Prinzip deutlich wird:

Wir haben hier folgende Records:

Code: Alles auswählen

type TFarbe = record
       rot: 0..255;
       gruen: 0..255;
       blau: 0..255;
    end;//endtype Farbe
 
  type TFunkList = record
       funktionsgleichung:string[255];
       IntMin, IntMax, unsharpnes, Strichbreite:extended;
       Farbe:TFarbe;
       aktiv:boolean;
    end;//endtype Tfunklist
und ein globales Array, dass über das Record TFunkList definiert ist:

Code: Alles auswählen

Funktionsliste:array of TFunkList;
Unsere Speicherroutine wird mit Hilfe des Lazarusinternen Save-Dialogs ausgeführt. Es sollen die Elemente des Arrays "Funktionsliste" in einem Dialog "speichern unter" gespeichert werden:

Code: Alles auswählen

procedure TFFunkEdit.MenuItem4Click(Sender: TObject);
var
i:integer;
Datei:File of TFunkList;  //Das "of TFunkList" ist wichtig.
begin
  If Savedialog1.Execute then begin
     Pfad:=Savedialog1.filename;  //Hier der Dateiname.
     Assignfile(Datei,Pfad);  //Dateivariable zuweisen
     Rewrite(Datei); //Datei zum schreiben öffnen.
     For i:=0 to length(Funktionsliste)-1 do begin
     write(Datei,Funktionsliste[i]);  //Hier wird das Record elementweise gespeichert
     end;
     closefile(Datei); //Datei schließen.
     FFunkedit.StatusBar1.Panels[0].Text:='Aktive Datei: ' +Pfad;
   end;
end;
Im Grunde also sehr elementare Befehle. Um die Datei wieder zu öffnen und die darin enthaltenen Daten wieder in das Array Funktionsliste vom Typ Record zu erhalten, benutzen wir folgenden Code:

Code: Alles auswählen

procedure TFFunkEdit.MenuItem2Click(Sender: TObject);
var
i:integer;
Datei:File of TFunkList;
begin
  If Opendialog1.Execute then begin  //Der Open-Dialog.
     Pfad:=openDialog1.filename;
     Assignfile(Datei,Pfad);  // Dateivariable zuweisen
     {$i-} Reset(Datei); {$i+} // Öffnet die Datei.
     if IOResult <> 0 then      // wenn keine Datei da ist
  begin
    Application.MessageBox('Die Datei konnte nicht ge�ffnet werden!','Fehler', 52);
    Exit
  end              else
  setlength(Funktionsliste,Filesize(Datei));  //Hier wird die Größe des Arrays entsprechend der gespeicherten Daten angepasse
  for i := 0 to Filesize(Datei)-1 do Read(Datei, Funktionsliste[i]);         // alle Datens�tze in das Array einlesen
  closefile(Datei);  /Hier wird die Datei wieder geschlossen.
 end;
So haben das jedenfalls wir (bzw. hauptsächlich der Arthur) gelöst.

Enthalten sind auch Routinen zum Fehler abfangen. Der komplette Code ist auf unserer Website (http://www.promathika.lazarusforum.de" onclick="window.open(this.href);return false;) einsehbar.

Ich hoffe, die Antwort hilft dir weiter.

Gruß, Euklid

Benutzeravatar
theo
Beiträge: 10927
Registriert: Mo 11. Sep 2006, 19:01

Beitrag von theo »

Christian hat geschrieben: Dann täuscht dich dein empfinden, mit TFilestream musst du beim lesen/schreiben immer die grösse des zu lesenden Blockes mitübergeben
Naja, hängt wohl vom Zweck ab. Mit "f : file of TProject;" ist man dafür recht eingeschränkt wenn man Daten mit nicht fixen Grössen wie AnsiStrings oder Bitmaps speichern will.

Dass es beim Suchen schneller ist, kann ich mir vorstellen. Beim reinen Vorwärts-Einlesen glaube ich aber nicht, dass es viel ausmacht. (Hab's allerdings auch nicht getestet)

Antrepolit
Beiträge: 340
Registriert: Di 12. Sep 2006, 08:57
OS, Lazarus, FPC: Winux (L 0.9.xy FPC 2.2.z)
CPU-Target: xxBit
Kontaktdaten:

Beitrag von Antrepolit »

Die "alten" Pascal-Routinen haben einen Vorteil:

Jeder, der mit Pascal angefangen hat, kennt sie, und würde
sie für das beschriebene Problem verwenden. Denn eines
gilt ja nach wie vor:

Warum mit Kanonen auf Spatzen schießen?

Records sind - da stimme ich Christian voll und ganz zu - der
Einfachste Weg, um Datensätze zu lesen, schreiben und zu finden.

Und leitet man sich Stream mal ein wenig ab, wird ersichtlich, dass
die Funktion für "Datenströme" implementiert wurde. Z. B. bei Filmen,
Musikdateien, ... alles wo man sehr viele Daten auf die Platte bannen muss.
Wie schon erwähnt erpart einem das Read(f,Data) einiges an Fehlersuche.

Ich denke, dass man gewissen Dingen die ursprüngliche Funktionalität
lassen sollte.
Grüße, Antrepolit

care only if your os is really burning

Christian
Beiträge: 6079
Registriert: Do 21. Sep 2006, 07:51
OS, Lazarus, FPC: iWinux (L 1.x.xy FPC 2.y.z)
CPU-Target: AVR,ARM,x86(-64)
Wohnort: Dessau
Kontaktdaten:

Beitrag von Christian »

W.m.k.A.h.e.m.F.h. -> http://www.gidf.de/

bembulak
Beiträge: 370
Registriert: Di 6. Feb 2007, 09:29
OS, Lazarus, FPC: L0.9.29 SVN:24607 FPC 2.4.0-32 bit @ Win XP SP3
CPU-Target: 32bit i386, ARM
Wohnort: Oberösterreich

Beitrag von bembulak »

Hallo!

Erstmal euch allen ein herzliches Dankeschön für eure Antworten. Ich muss das jetzt wirken lassen, also mal lesen, ausprobieren und vor allem verstehen.
Ich komme aber sicher noch einmal darauf zurück.

Vielen Dank!

Benutzeravatar
theo
Beiträge: 10927
Registriert: Mo 11. Sep 2006, 19:01

Beitrag von theo »

Ja, das glaube ich sofort. Das ist aber alles eine Frage des Puffers.
Wer mit TFileStream Daten Byte bzw Char-weise einliest braucht sich nicht wundern, wenn's langsam ist.
Dass es nicht prinzipiell am TFileStream liegt, beweisen ja auch die Ergebnisse von TFileStream / TReader.

Benutzeravatar
theo
Beiträge: 10927
Registriert: Mo 11. Sep 2006, 19:01

Beitrag von theo »

Antrepolit hat geschrieben:Warum mit Kanonen auf Spatzen schießen?
Kann deinen Argumenten nicht folgen. Was ist denn an TFileStream kanonig?
Antrepolit hat geschrieben: Records sind - da stimme ich Christian voll und ganz zu - der
Einfachste Weg, um Datensätze zu lesen, schreiben und zu finden.
Hab ich schon gesagt. Für fixe Datengrössen ja, für lange Strings etc. nicht.
Antrepolit hat geschrieben: Und leitet man sich Stream mal ein wenig ab, wird ersichtlich, dass
die Funktion für "Datenströme" implementiert wurde. Z. B. bei Filmen,
Musikdateien, ... alles wo man sehr viele Daten auf die Platte bannen muss.
Schmarrn. Das hat doch mit der Datenmenge nix zu tun.

Antworten