![]() |
File of eigener Typ
Mein Problem: ich will eine Art Verwaltungsprogramm schreiben, welches Daten zu einer Reihe gelesener Bücher speichert. Dabei ergeben sich 2 Probleme bei der Speicherung dieser Daten:
1: ich habe für die Daten der Bücher einen eigenen REcord geschrieben und wenn ich das jetzt in einem File speichern will meckert der Compiler... was tun?
Delphi-Quellcode:
Fehler: Type 'TBuecherliste' needs finalization...
TNote = 1..6;
buch = record Titel: string[50]; Autor: string[50]; Erscheinungsjahr: TDate; DatumEintrag: TDate; ISBN: Integer; Bewertung: TNote; Kommentar: string[100]; Privat: Boolean; end; TBuecherliste = array of buch; // .... Datei: file of TBuecherliste; // Fehler 2: Diese Datei soll auf einer zentralen Festplatte im Netzwerk liegen, auf die verschiedenen User mit diesem Programm zugreifen. Wie kann ich erkenne ob die Datei gerade genutzt wird? Denn mehrere Nutzer können ja nicht gleichzeitig in eine Datei schreiben oder? |
Re: File of eigener Typ
Delphi-Quellcode:
Denn du willst ja Bücher und keine Bücherlisten speichern.
Datei: file of TBuch;
Da TBuecherliste ein dynamische Array ist, kann den Speicherbedarf nicht ermittelt werden |
Re: File of eigener Typ
OK danke... Denkfehler... ich kann ja trotzdem mehrere Datensätze reinschreiben. Doch jetzt meckert er bei öffnen der Datei:
Delphi-Quellcode:
Beim assign meckert er incompatible types und später beim schreiben auch.
Buecher: array of buch;
procedure TForm1.datenladen; // Alle Daten aus der Datei einlesen var I: Integer; begin AssignFile(Datei,speicherort); // Datei anlegen Reset(Datei); SetLength(Buecher,0); // Bücher zurücksetzen i := 0; while not Eof(Datei) do // Daten einlesen begin SetLength(Buecher,Length(Buecher)+1); Readln(Datei,Buecher[i]); Inc(i); end; CloseFile(Datei); end; |
Re: File of eigener Typ
Ist Datei genauso deklariert?
ReadLn() funktioniert nur bei Textdateien ( TextFile) |
Re: File of eigener Typ
Guten Abend,
ist im Speicherort auch der Name der Datei enthalten, oder nur der Pfad? Zitat:
SetLength nicht in Schritten von 1 zu erhöhen. Setze SetLength auf einen größeren Wert und prüfe wenn Du das Array füllst ob das Array bereits komplett gefüllt ist wenn ja vergrößerst Du es wieder. Eventuell würde ich auch über ![]() Grüße Klaus Grüße Klaus |
Re: File of eigener Typ
Ja das ist so deklariert. Das mit dem setlength merk ich mir danke ;). Hier einfach mal der gesamt bisherige Text.
Delphi-Quellcode:
unit mMain;
interface uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs; type TNote = 1..6; buch = record Titel: string[50]; Autor: string[50]; Erscheinungsjahr: TDate; DatumEintrag: TDate; ISBN: Integer; Bewertung: TNote; Kommentar: string[100]; Privat: Boolean; end; TBuecherliste = array of buch; TForm1 = class(TForm) procedure FormCreate(Sender: TObject); private { Private declarations } procedure datenladen; procedure datenspeichern; procedure datenzeigen(SortiertNach: string); procedure encrypt(schluessel: string); procedure decrypt(schluessel: string); procedure neuerWert; procedure WertLoeschen; procedure WertAendern(); function benutzer: Boolean; function passwort: Boolean; function speicherort: Boolean; public { Public declarations } end; var Form1: TForm1; Buecher: array of buch; passwort: string; benutzer: string; speicherort: string; Datei: file of buch; implementation {$R *.dfm} procedure TForm1.datenladen; // Alle Daten aus der Datei einlesen var I: Integer; begin AssignFile(Datei,speicherort); // Datei anlegen // FEHLER hier Reset(Datei); SetLength(Buecher,0); // Bücher zurücksetzen i := 0; while not Eof(Datei) do // Daten einlesen begin SetLength(Buecher,Length(Buecher)+1); Read(Datei,Buecher[i]); Inc(i); end; CloseFile(Datei); end; procedure TForm1.datenspeichern; // Alle Daten speichern var I: Integer; begin AssignFile(Datei,speicherort); // Datei anlegen // FEHLER HIER Rewrite(Datei); i := 0; while not Eof(Datei) do // Daten schreiben begin Writeln(Datei,Buecher[i]); // FEHLER HIER Inc(i); end; CloseFile(Datei); end; procedure TForm1.datenzeigen(SortiertNach: string); begin end; procedure TForm1.decrypt(schluessel: string); begin end; procedure TForm1.encrypt(schluessel: string); begin end; procedure TForm1.FormCreate(Sender: TObject); var clickedok: Boolean; begin // Sachen abfragen end; procedure TForm1.neuerWert; begin end; function TForm1.passwort: Boolean; // Passwort erfragen begin Result := InputQuery('Eingabe','Benutzernamen eingeben',passwort); // Wenn ok gedrückt wird = true; bei abbrechen false end; function TForm1.speicherort: Boolean; // Speicherort erfragen begin Result := InputQuery('Eingabe','Benutzernamen eingeben',speicherort); end; function TForm1.benutzer: Boolean; // Benutzer erfragen begin Result := InputQuery('Eingabe','Benutzernamen eingeben',benutzer); end; procedure TForm1.WertAendern; begin end; procedure TForm1.WertLoeschen; begin end; end. |
Re: File of eigener Typ
Delphi-Quellcode:
und
function speicherort: Boolean;
Delphi-Quellcode:
Passt nicht so ganz, oder?
AssignFile(Datei,speicherort);
Grüße Klaus |
Re: File of eigener Typ
Und mach auch bei dem Writeln das ln weg, dann kommt da kein Fehler mehr :)
|
Re: File of eigener Typ
Ah ok Speicherort gibts als Funktion und als String... danke ;-).
Jetzt gehts... erstmal^^. |
Re: File of eigener Typ
Zitat:
Das ist auch einer der Gründe, warum man keine globalen Variablen verwenden soll. a) hätte hier der Compiler die doppelten Namen entdeckt und gemeckert b) ist vorallem die Datei eine lokale Variable, welche nur in datenladen verwendet wird (dort initialisiert und auch freigegeben) ... sowas gehört auch nur lokal und hat absolut nix global zu suchen :warn: (genauso wie das I, wobei dort bestimmt der Compiler gemeckert hat ... oder warum ist Diese mal zur Abwechslung lokal deklariert? )
Delphi-Quellcode:
TForm1 = class(TForm)
procedure FormCreate(Sender: TObject); private { Private declarations } Buecher: array of buch; passwort: string; benutzer: string; speicherort: string; procedure datenladen; procedure datenspeichern; procedure datenzeigen(SortiertNach: string); procedure encrypt(schluessel: string); procedure decrypt(schluessel: string); procedure neuerWert; procedure WertLoeschen; procedure WertAendern(); function benutzer: Boolean; function passwort: Boolean; function speicherort: Boolean; public { Public declarations } end; implementation {$R *.dfm} procedure TForm1.datenladen; // Alle Daten aus der Datei einlesen var I: Integer; Datei: file of buch; begin AssignFile(Datei,speicherort); // Datei anlegen // FEHLER hier Reset(Datei); SetLength(Buecher,0); // Bücher zurücksetzen i := 0; while not Eof(Datei) do // Daten einlesen begin SetLength(Buecher,Length(Buecher)+1); Read(Datei,Buecher[i]); Inc(i); end; CloseFile(Datei); end; Mal so als Frage: Wer hat dir denn beigebracht, so daß du die ganzen Variablen global neben dem Form1 abgelegt hast? (gib demjenigen bitte mal 'ne Ohrfeige und schick ihn/sie hier bei uns vorbei) |
Re: File of eigener Typ
Hallo eragon123,
für die Deklaration des Records sollte man, wenn man in speichert mit packed arbeiten. In Deinen Beispiel würde man 7Byte pro Datensatz sparen. Also der Record sollte so aussehen:
Delphi-Quellcode:
Zudem ist eine Fehlerbehandlung bei I/O-Operationen auch nicht verkehrt(eigentlich zwingend notwendig).
buch = packed record
Titel: string[50]; Autor: string[50]; Erscheinungsjahr: TDate; DatumEintrag: TDate; ISBN: Integer; Bewertung: TNote; Kommentar: string[100]; Privat: Boolean; end; Bis bald Chemiker |
Re: File of eigener Typ
Zitat:
also sobald Daten den Prozess/die Programminstanz, das Modul (EXE/DLL) verlassen. Ebenso sollte man nur statische Typen nehmen. z.B. Integer und Char können sich ja schnell mal in der nächsten Delphi-/Compilerversion verändern (ein gutes Beispiel sind die vielen Unicode-Probleme mit alten Programmen/Codes, seit Delphi 2009) Zitat:
Delphi-Quellcode:
Für 'nen 32-Bit-Prozess also eine passende Ausrichtung.
TNote = 1..6;
TBuch = packed record 8 Byte-Align 4 Byte Titel: String[51]; // 52 Byte = 6,5 > 6,5 = 13 >13 Autor: String[51]; // 52 Byte = 6,5 >13 = 13 >26 Erscheinungsjahr: TDate; // 8 Byte = 1 >14 = 2 >28 DatumEintrag: TDate; // 8 Byte = 1 >15 = 2 >30 ISBN: LongInt; // 4 Byte = 0,5 >15,5 = 1 >31 Kommentar: String[105{oder 97}]; // 106 Byte = 13,25 >28,75 = 26,5 >57,5 Bewertung: TNote; // 1 Byte = 0,125>28,875 = 0,25>57,75 Privat: Boolean; // 1 Byte = 0,125>29 = 0,25>58 end; |
Re: File of eigener Typ
Hier mal eine effektivere Variante zum Einlesen:
Delphi-Quellcode:
Array und Record müssen natürlich angepasst werden. Ich hab das mal eben aus einer Anwendung von mir rauskopiert :-)
procedure LoadArray; // Datei öffnen
var i, anzahl: Integer; f: TFileStream; begin if FileExists(Path+OpenName) then begin //Path und OpenName sind Verzeichnis und Dateiname try f := TFileStream.Create(Path+OpenName, fmOpenRead) except ShowMessage('Die Datei konnte nicht geöffnet werden!'); Exit; end; f.ReadBuffer(Anzahl, SizeOf(Integer)); //anzahl wird hier mit der Anzahl der Records geladen SetLength(MeinArray, Anzahl); //und kann direkt für SetLenghth genutzt werden for i := 0 to Anzahl-1 do f.ReadBuffer(MeinArray[i],SizeOf(TMeinRec)); // alle Records einlesen f.Free; end; end; |
Re: File of eigener Typ
effktiver =
Delphi-Quellcode:
noch was zur Fehlerbehandlung:
f.ReadBuffer(MeinArray[0],SizeOf(TMeinRec) * Anzahl); // alle Records einlesen
- TFileStream.Create gibt schon eine gute Fehlermeldung aus ... diese muß ja nicht unbedingt zerstören - und was passiert mit "f", wenn ReadBuffer oder SetLength eine Exception werfen?
Delphi-Quellcode:
oder
if FileExists(Path+OpenName) then begin //Path und OpenName sind Verzeichnis und Dateiname
f := TFileStream.Create(Path+OpenName, fmOpenRead) try f.ReadBuffer(Anzahl, SizeOf(Integer)); //anzahl wird hier mit der Anzahl der Records geladen SetLength(MeinArray, Anzahl); //und kann direkt für SetLenghth genutzt werden f.ReadBuffer(MeinArray[0],SizeOf(TMeinRec) * Anzahl); // alle Records einlesen finally f.Free; end; end else raise Exception.CreateFmt('Datei "%s" existiert nicht.', [OpenName]);
Delphi-Quellcode:
if FileExists(Path+OpenName) then begin //Path und OpenName sind Verzeichnis und Dateiname
f := TFileStream.Create(Path+OpenName, fmOpenRead) try Anzahl := f.Size div SizeOf(TMeinRec); // Anzahl wird aus der Dateigröße berechnet SetLength(MeinArray, Anzahl); //und kann direkt für SetLenghth genutzt werden f.ReadBuffer(MeinArray[0], SizeOf(TMeinRec) * Anzahl); // alle Records einlesen finally f.Free; end; end else raise Exception.CreateFmt('Datei "%s" existiert nicht.', [OpenName]); |
Re: File of eigener Typ
Das war auch nur ganz flüchtig rauskopiert.
Mir ging es eigentlich nur darum, dieses (sehr uneffektive) schrittweise SetLength zu eliminieren :-) |
Re: File of eigener Typ
Hallo himitzu,
Zitat:
Zudem ist man teilweise an die Größe von Feldern gebunden. Für eine deutsche PLZ braucht man nicht mehr als 5 Stellen(es sei denn wir kaufen demnächst Griechenland und Spanien usw.). Bis bald Chemiker |
Re: File of eigener Typ
Man kann die Werte ja nach unten runden (bei 32 Bit reicht es ja auch, wenn man auf 4 Byte ausrichtet)
Füllfelder und eine andere Anorfnung der Felder machen schon viel aus, aber nja Hauptsache ist ja, daß man bei sowas Packed und feste Typen verwendet und der Rest ist Optimierungssache, welche man notfalls auch mal weglassen kann wenn es einem nicht so wichtig ist. :stupid: |
Alle Zeitangaben in WEZ +1. Es ist jetzt 08:28 Uhr. |
Powered by vBulletin® Copyright ©2000 - 2025, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024-2025 by Thomas Breitkreuz