Datenspeicherung allgemein

Für Fragen von Einsteigern und Programmieranfängern...
Antworten
BernhardDEL
Beiträge: 36
Registriert: Di 31. Jan 2017, 17:18

Datenspeicherung allgemein

Beitrag von BernhardDEL »

Hallo zusammen !

Ich mal wieder mit einem Problem.

Nachdem ich nun ein Buch zu Lazarus habe (von Wilfred Koch) bin ich dann auch gleich dazu übergegangen mich mit dem Abspeichern und Einlesen von Daten zu befassen.
Streng nach Lehrbuch habe ich nachstehenden Code geschrieben :

. Unit Unit1;
. uses
. Classes, SysUtils, FileUtil, Forms, Controls, Graphics, Dialogs, ActnList,
. StdCtrls;
. type
. Type_PLZ_Stadt = record
. Record_PLZ : Integer;
. Record_Stadt : String;
. end;
.
. File_PLZ_Stadt = file of Type_PLZ_Stadt;
.
. { TGrundform }
. TGrundform = class(TForm)
. PLZ : TEdit;
. Stadt : TEdit;
. Lesen: TButton;
. Speichern: TButton;
.
. private
. { private declarations }
. public
. { public declarations }

. end;
.
. Procedure Datensatz_sichern;
. Procedure Datensatz_holen;
.
. var
.
. Grundform: TGrundform;
. PfadPLZ_Stadt : String = 'C:/CTS-Soft/Dateien/PLZ_Stadt.dat';
. PLZ_Stadt : Type_PLZ_Stadt;
. DPLZ_Stadt : File_PLZ_Stadt;
. DSatz : Integer = 0;
.
. implementation
.
. {$R *.lfm}

Soweit so schön und die Deklaration ist auch so wie im Buch.

Beim Kompilieren jedoch meckert der Compiler die oben rot markierte Zeile an (hervorgehoben der Eintrag "Type_PLZ_Stadt") mit der Meldung :

Projekt kompilieren, Ziel: C:\Users\Bernhard\AppData\Local\Temp\project1.exe: Exit code 1, Fehler: 1
unit1.pas(18,42) Error: Typed files cannot contain reference-counted types.

Ich kann damit leider nichts anfangen.

Kann mir jemand auf die Sprünge helfen ???

Bernhard

Michl
Beiträge: 2505
Registriert: Di 19. Jun 2012, 12:54

Re: Datenspeicherung allgemein

Beitrag von Michl »

Das Beispiel funktioniert nur, wenn du Strings verwendest, die als ShortString definiert sind. Diese enthalten dann eine feste Größe bis maximal 255 Zeichen: http://wiki.freepascal.org/Character_and_string_types/de#ShortString

Also entweder so:

Code: Alles auswählen

unit Unit1;
 
{$mode objfpc}{$H+}
...
type
  Type_PLZ_Stadt = record
    Record_PLZ : Integer;
    Record_Stadt : ShortString;
  end;
 
  File_PLZ_Stadt = file of Type_PLZ_Stadt; 

oder du stellst per Compilerswitch um, daß für Strings, ShortStrings verwendet werden:

Code: Alles auswählen

unit Unit1;
 
{$mode objfpc}{$H-}  // mit $H- ist String = ShortString
...
type
  Type_PLZ_Stadt = record
    Record_PLZ : Integer;
    Record_Stadt : String;
  end;
 
  File_PLZ_Stadt = file of Type_PLZ_Stadt;   

Code: Alles auswählen

type
  TLiveSelection = (lsMoney, lsChilds, lsTime);
  TLive = Array[0..1] of TLiveSelection; 

Warf
Beiträge: 1908
Registriert: Di 23. Sep 2014, 17:46
OS, Lazarus, FPC: Win10 | Linux
CPU-Target: x86_64

Re: Datenspeicherung allgemein

Beitrag von Warf »

Dynamische Arrays und Dynamische Strings können nicht einfach so gesichert werden. Hier mal das Kapitel über Dateien aus meinem Pascal Tutorial bei dem ich im letzen abschnitt genau darauf eingehe (rechtschreibfehler sind gratis):

Das letzte Kapitel zum Klassischen Pascal geht um das Thema Dateien. Wie Bekannt sein sollte Speichert der Computer Informationen dauerhaft in Form von Dateien in einem Dateisystem. Dabei hat jede Datei einen Namen, und einen Pfad zu dem Ordner der die Datei enthält. Je nach Betriebsystem unterscheidet sich die Darstellung von Dateiname und Pfad. Windows z.B. stellt jedes Speichermedium als Laufwerk da, welches mit einem Buchstaben und einem Doppelpunkt gekennzeichnet ist (C:, E:, D:), und dann mit Backslashes getrennt die Ordnerstruktur des Pfades, und am Ende der Dateiname, z.B.

Code: Alles auswählen

C:\Pfad\Zur\Datei.ext

Unix Systeme hingegen Haben ein so genanntes Root Directory, der Basispfad des Speichermediums auf dem das System Installiert ist, und weitere Speichermedien werden als Ordner in diese Struktur eingehängt. Die Darstellung beginnt mit einem Slash, und darauf werden alle Ordner mit Slashes getrennt und am Ende folgt der Dateiname. Z.B:

Code: Alles auswählen

/Pfad/Zur/Datei.ext

Zur Kategoriesierung der Dateien werden u.a. Dateiendungen verwendet, welche mit einem Punkt getrennt an den Dateinamen angehängt werden (um Beispiel .ext). Es gibt keine wirklichen Vorgaben an die Dateiendungen, und die können auch theoretisch beliebig verwendet werden, allerdings wird meißt eine aussagekräftige Abkürzung für den Dateiinhalt verwendet. So steht z.B. .txt für Textdateien, .exe für Windows Executeables, und .bmp für Bitmap.

Konvention: Bei dem schreiben von eigenen Dateieformaten sollte man eine vernünftige Dateiendung wählen, so behält man selbst, oder andere die sich die Programme ansehen einen Überblick darüber was für ein Inhalt sich hinter einer Datei verbirgt. Meißt werden zur nomenklatur 3-4 Buchstaben lange Abkürzungen englischer Wörter verwendet wie .bin für binäre Dateien, .lst für Listen, .cfg für Konfigurationsdateien. Man sollte auch versuchen keine Etablierten Dateiendungen zu verwenden (wie .jpg für Textdokumente).

Dateitypen und Aufbau

Grundsätzlich unterscheiden wir zwischen zwei Typen von Dateien, Textdateien und Binärdateien, da diese sich im Umgang unterscheiden.
Textdateien sind Dateien mit für Menschen lesbaren Buchstaben. Textdateien werden entweder Zeilenweise, Wort für Wort, oder jeder Charakter einzeln gelesen.

Binärdateien hingegen werden Byteweise beschrieben. In Binärdateien werden die Werte in dem selben Byteformat wie die Pascal Datentypen gespeichert.
Da Binäredatentypen im gegensatz zu Textdateien nicht über einen einfachen Syntaxcheck verifiziert werden können, und die Bytes in verschiedenen Datentypen komplett andere Werte hat, verwendet man einen Header, um Informationen über den Inhalt zu stellen. Als Teil dieses Headers verwendet man oftmals eine so genannte Magic Number, eine Zahl, die den Datentypen Beschreibt. Wenn die Magic Number nicht der erwartete Wert ist weiß man dass es nicht der richtige Datentyp ist. Im Umkehrschluss kann man, bei mehreren Dateien mit gleicher Dateiendung über die Magic Number feststellen was der Inhalt dieser Dateien ist.

Pascal kennt außerdem noch Typisierte Binärdateien, das sind Binärdateien die nur einen Datentypen enthalten können, z.B. nur Integer Werte.

Dateien in Pascal

Um Dateien zu verwenden nutzt man in Pascal so genannte File Handles, das sind Betriebsystem interne Kennnummern für die geöffneten Dateien. Der Variablentyp für ein solches Dateihandle ist:
TextFile für Textdateien
File of Typ für typisierte Binärdateien
File für untypisierte Binärdateien.

Egal welcher Art, die Dateihandles bekommt man via AssignFile, muss sie mit CloseFile wieder Freigeben.
Genau wie bei Zeigern empfiehlt sich hier die Nutzung eines Try-Finally Blocks.

Code: Alles auswählen

var F: TextFile;
begin
  AssignFile(F, 'Pfad/Zur/Datei.ext');
  try
 
  finally
    CloseFile(F);
  end;
end;


Mit dem AssignFile wird das Handle zugewiesen, danach muss die Datei allerdings noch geöffnet werden. Das öffnen geschieht über Reset, Rewrite oder Append.
Append ist zum Schreiben am Ende von Textdateien, also zum Anhängen von Texten. Rewrite erstellt eine neue Datei, exsistiert die Datei bereits wird sie überschrieben. Reset öffnet Textdateien zum lesen, oder Binärdateien zum Lesen und/oder Schreiben.

Nun gehe ich näher auf die einzelnen Typen ein.

Textdateien

Textdateien ließt und schreibt man genau wie mit der Konsole. Man verwendet Read und ReadLn bzw Write und WriteLn:

Code: Alles auswählen

Read(FileHandle, Variable); // Lese Inhalt von FileHandle in Variable
ReadLn(FileHandle, Variable); // Lese eine Zeile in Variable
Write(FileHandle, Wert); // Schreibt Wert in die Textdatei
WriteLn(FileHandle, Wert); // Schreibt Wert+Zeilenumbruch in Textdatei


Am besten erklärt das ein Beispiel Code:

Code: Alles auswählen

program ReadText;
 
{$Mode ObjFPC}{$H+}
 
var
  F: TextFile;
  i: Integer;
  s: String;
  d: Double;
begin
  // Variablen über Konsole einlesen
  ReadLn(i);
  ReadLn(s);
  ReadLn(d);
  AssignFile(F, 'datei.txt');
  try
    Rewrite(F);
    // Zeilenweise Informationen Schreiben
    WriteLn(F, i);
    WriteLn(F, s);
    WriteLn(F, d);
  finally
    CloseFile(F);
  end;
  // Daten Lesen
  AssignFile(F, 'datei.txt');
  try
    Reset(F);
    // Zeilenweise auslesen
    ReadLn(F, i);
    ReadLn(F, s);
    ReadLn(F, d);
  finally
    CloseFile(F);
  end;
  // Daten Ausgeben
  WriteLn(i);
  WriteLn(s);
  WriteLn(d);
end.


Dieses Programm sichert einen String, einen Integer und einen Double Zeilenweise in einer Textdatei und ließt diese anschließend wieder aus. Der Dateiinhalt ist mit 123 als Integer, String als String und 3.5 als Double dann:

Code: Alles auswählen

123
String
 3.5000000000000000E+000


Viel zu beachten gibt es an dieser Stelle eher nicht, da wir diese Form des Lesen und Schreiben schon von der Konsole kennen.

Typisierte Binärdateien

Bei Typisierten Binärdateien gibt man bei der Definition der Handle Variable einen Datentypen an, je nach dem was der Inhalt der Datei sein soll.

Binäre Dateien kann man mittels Reset in verschiedenen Modi öffnen. Dafür stellt Pascal die Globale Variable FileMode bereit. Diese kann man vor dem verwenden von Reset auf 3 verschiedene Werte setzen:

Code: Alles auswählen

fmOpenRead = 0
fmOpenWrite = 1
fmOpenReadWrite = 2

Um die Datei entsprechend zum Lesen, Schreiben oder beidem zu öffnen.
Wenn man eine Datei zum Schreiben öffnet, ohne die entsprechende Berechtigung im System zu haben wirft dies einen Fehler.

Hinweis: Wenn man die Variable FileMode nicht setzt so wird die Datei mit dem Default Modus fmOpenReadWrite geöffnet.

Bei typisierten binären Dateien werden Informationen via Read und Write geschrieben.
In dem folgenden Beispiel wird ein Record in eine Binärdatei geschrieben:

Code: Alles auswählen

program BinFileTest;
 
{$MODE ObjFPC}{$H+}
uses SysUtils;
 
type
  // Record der gesichert werden soll
  TMyRec = record
      MyInt1, MyInt2: Integer;
      MyStr: ShortString;
    MyDouble: Double;
   end;
 
var
  // FileHandle
  F: File of TMyRec;
  // Record Variable
   Rec: TMyRec;
 
begin
  // Record befüllen
  Rec.MyInt1 := 5;
   Rec.MyInt2 := 10;
   Rec.MyStr := 'Hallo Welt'; // ShortString statische zusammengesetze Struktur
   Rec.MyDouble := 3.5;
 
  // Datei öffnen
   AssignFile(F, 'Test.bin');
  try
    Rewrite(F);
    // Record schreiben
    Write(F, Rec);
  finally
    CloseFile(F);
  end;
 
  // Record Löschen
  FillChar(Rec, SizeOf(Rec), #00);
 
  // Datei wieder öffnen
   AssignFile(F, 'Test.bin');
  try
    // FileMode setzen
    FileMode := fmOpenRead;
    Reset(F);
    // Daten Lesen
    Read(F, Rec);
  finally
    CloseFile(F);
  end;
 
  // Daten Ausgeben
   WriteLn(Rec.MyInt1);
   WriteLn(Rec.MyInt2);
   WriteLn(Rec.MyStr);
   WriteLn(Rec.MyDouble);
end.


Bemerkung: In diesem Beispiel verwende ich ShortString, da ShortStrings als statisch zusammengesetze Datenstruktur sich einfach Byteweise sichern lassen. Bei Dynamischen Strings würde lediglich der Zeiger auf den String gesichert werden.

Als Typen für diese Dateien kann man letztlich alles nehmen, außer Zeiger, Dynamische Arrays oder Dynamische Strings, da man damit nur den Zeiger Speichern würde, welcher beim nächsten Durchlauf natürlich nicht unbedingt der Selbe sein muss. Statische Arrays, ShortStrings, Records oder andere Ordinärypen lassen sich aber ohne Probleme speichern.

Untypisierte Binärdateien

Schlussendlich gibt es noch untypisierte Binärdateien. In diese kann man beliebige binäre Daten sichern.
Dafür übergibt man der Funktion Reset bzw. Rewrite neben dem FileHandle noch einen weiteren Parameter, RecordSize. Diese gibt an in wie großen Blöcken geschrieben bzw gelesen werden soll. Schreibt man z.B. nur Integer (4 Byte) und Double (8 Byte) in eine Datei kann man eine RecordSize von 4 Byte wählen. Will man z.B. Bytes, oder genaue Größen wie von Packed Records sichern sollte man eine RecordSize von 1 Byte wählen.

Gelesen und Geschrieben wird dann mit den Funktionen:

Code: Alles auswählen

BlockRead(FileHandle, Buffer, Count, ReadCount);
BlockWrite(FileHandle, Buffer, Count, WriteCount);


Wobei WriteCount bzw. ReadCount Optionale Var Parameter sind. Buffer is auch ein Var Parameter, über welchen die Variable übergeben wird deren Inhalt geschrieben werden soll, oder in die gelesen werden soll. Count gibt an wie viele Einheiten der über Reset bzw. Rewrite RecordSize größe gelesen werden soll. Wenn eine Optionale Variable ReadCount bzw. WriteCount übergeben wird, wird dort gespeichert wie viele Einheiten tatsächlich gelesen wurden (im Fall dass man versucht mehr Bytes zu lesen als die Datei enthält).

Bemerkung: Will man größere Datenmengen in einen Array oder String laden, so empfiehlt sich die Elemente in größeren Blöcken (z.B. 1024 Bytes) zu lesen, und über ReadCount kann man dann überprüfen ob man am Ende der Datei angekommen ist.

Ein kleines Beispiel:

Code: Alles auswählen

program BinFileTest;
 
{$MODE ObjFPC}{$H+}
uses SysUtils;
 
var
  F: File;
  i: Integer;
  s: ShortString;
  d: Double;
 
begin
  i := 4;
  s := 'Hallo Welt';
  d := 3.5;
   AssignFile(F, 'Test.bin');
   try
    Rewrite(F, 1); // 1 Byte RecordSize
    BlockWrite(F, i, SizeOf(Integer)); // Integer Schreiben
    BlockWrite(F, s, SizeOf(s)); // ShortString schreiben
    BlockWrite(F, d, SizeOf(d)); // Double schreiben
  finally
     CloseFile(F);
   end;
 
   AssignFile(F, 'Test.bin');
   try
     FileMode := fmOpenRead; // Lesen
    Reset(F, 1); // 1 Byte RecordSize
    BlockRead(F, i, SizeOf(Integer)); // Integer Schreiben
    BlockRead(F, s, SizeOf(s)); // ShortString schreiben
    BlockRead(F, d, SizeOf(d)); // Double schreiben
  finally
     CloseFile(F);
   end;
 
   WriteLn(i);
   WriteLn(s);
   WriteLn(d);
end.
 


Dabei wird ein Integer, ein Double und ein ShortString in die Binärdatei geschrieben.


Generell lässt sich über die Verschiedenen Dateien sagen, Binärdateien sind deutlich schneller zu lesen, dafür aber nicht mit normalen Editoren veränderbar. Textdateien sind für jeden Nutzer gut Lesbar und Editierbar. Typisierte Binärdateien sind sehr einfach zu verwenden, und solange man nur einzelne Datentypen sichert eine sehr schnelle einfache Möglichkeit, die vor allem kürzer ist als die untypisierte Lösung.


Weitere Dateioperationen

Zum arbeiten mit Dateien gibt es noch mehr Funktionen:

function Eof(FileHandle): Boolean;
Diese Funktion gibt True zurück wenn das FileHandle auf das Ende der Datei zeigt.

Code: Alles auswählen

While not Eof(F) do
begin
  // Zeilenweise Auslesen und ausgeben
  ReadLn(F, s);
  WriteLn(s);
end;


procedure Seek(FileHandle; Pos);
Diese Funktion setzt die Position des FileHandle auf Pos. Pos gibt die Position als Einheiten in RecordSize an oder in Größe des Typs bei typisierten Dateien. Seek funktioniert nicht auf Textdateien, sondern nur auf Binären Dateien.

Code: Alles auswählen

Seek(F, 0);
Read(F, i); // Erstes Element auslesen
Seek(F, 2);
Read(F, i); // Drittes Element auslesen


function FilePos(FileHandle): Integer;
Diese Funktion gibt die aktuelle Position des FileHandles zurück. Wie bei Seek auch wird die Position als Einheiten in Record bzw. Typgrößen angegeben.

Code: Alles auswählen

Seek(F, FilePos(F) - 1);
Read(F, i); // Vorheriges Element auslesen



Arrays, Strings und Zeiger schreiben
Zuletzt möchte ich noch kurz darauf eingehen wie man Arrays und Strings in Binärdateien schreibt. Bei statischen Arrays und ShortStrings ist das absolut kein Problem, und die können genau so wie andere Variablen auch geschrieben werden. Dynamische Strings und Arrays allerdings sind letztlich Zeiger, und würde man diese einfach so schreiben würde das nur den Zeiger in die Datei schreiben, welcher beim nächsten lesen wahrscheinlich nicht mehr gültig ist, oder vielleicht auf ein Komplett anderes Element zeigt. Dafür sehen wir uns zunächst einmal an wie wir Zeiger schreiben würden:

Code: Alles auswählen

var PInt1: PInteger;
  F: File;
begin
  // Initialisierung von F und PInt1
  BlockWrite(F, PInt1^, SizeOf(Integer));
  //...
  BlockRead(F, PInt1^, SizeOf(Integer));
end;


Durch den Dereferenzierungsoperator ^ geben wir an dass wir den Inhalt auf den der Zeiger zeigt schreiben wollen, bzw. das Gelesene das Element hinter dem Zeiger schreiben wollen.

Bei Arrays oder Strings können wir es genauso machen, wir können diese einfach in einen Pointer Casten und dann über ^ Schreiben lassen. Da die Länge allerdings Dynamisch ist müssen wir noch dazu speichern wie viele Elemente wir schreiben werden. Außerdem müssen wir bei dem Count Parameter noch die Anzahl an Elementen und ihre Größe berücksichtigen:

Code: Alles auswählen

var
  arr: Array of Integer;
  Str: AnsiString;
  Len: Integer;
  F: File;
begin
  // Initialisierung
  // Array schreiben
  Len := Length(arr);
  BlockWrite(F, Len, SizeOf(Len));
  BlockWrite(F, PInteger(arr)^, Len * SizeOf(Integer));
  // String Schreiben
  Len := Length(Str);
  BlockWrite(F, Len, SizeOf(Len));
  BlockWrite(F, PChar(Str)^, Len * SizeOf(Char));
end;


vor dem Lesen müssen wir dann die Länge auslesen und den Array/String mit SetLength auf diese Länge setzen:

Code: Alles auswählen

var
  Arr: Array of Integer;
  Str: AnsiString;
  Len: Integer;
begin
  // Initialisierung
  // Array Lesen
  BlockRead(F, Len, SizeOf(Len));
  SetLength(arr, Len);
  BlockRead(F, PInteger(arr)^, Len * SizeOf(Integer));
  // String Lesen
  BlockRead(F, Len, SizeOf(Len));
  SetLength(Str, Len);
  BlockRead(F, PChar(Str)^, Len * SizeOf(Char));
end;


Eine andere Möglichkeit ist es statt Zeiger zu nehmen Arrayelemente zu Adressieren:

Code: Alles auswählen

var
  arr: Array of Integer;
  Str: AnsiString;
  Len: Integer;
  F: File;
begin
  // Initialisierung
  // Array schreiben
  Len := Length(arr);
  BlockWrite(F, Len, SizeOf(Len));
  BlockWrite(F, arr[0], Len * SizeOf(Integer));
  // String Schreiben
  Len := Length(Str);
  BlockWrite(F, Len, SizeOf(Len));
  BlockWrite(F, Str[1], Len * SizeOf(Char));
  // Array Lesen
  BlockRead(F, Len, SizeOf(Len));
  SetLength(arr, Len);
  BlockRead(F, arr[0], Len * SizeOf(Integer));
  // String Lesen
  BlockRead(F, Len, SizeOf(Len));
  SetLength(Str, Len);
  BlockRead(F, Str[1], Len * SizeOf(Char));


Damit können auch sehr gut nur Teile eines Arrays oder Strings gelesen bzw. geschrieben werden. Allerdings hat das den Nachteil, wenn man mit einem Debugger arbeitet (z.B. dem GDB) welcher mit Array RangeChecks arbeitet kann es vorkommen, dass dieser Dabei Fehler wirft, weil man bei einem Leeren Array kein Element Adressieren sollte (auch wenn in diesem Fall eh kein Zugriff geschehen würde da Len = 0 wäre). Also ein Fehlarlam, welcher dennoch sehr nervig sein kann.

BernhardDEL
Beiträge: 36
Registriert: Di 31. Jan 2017, 17:18

Re: Datenspeicherung allgemein

Beitrag von BernhardDEL »

Puh - da hab ich erstmal was zu lesen :wink:

Danke für die Aufklärung. Habe es mit begrenzten Strings versucht, also String[40], und es geht.

Danke nochmal.

Bernhard

Mathias
Beiträge: 6160
Registriert: Do 2. Jan 2014, 17:21
OS, Lazarus, FPC: Linux (die neusten Trunk)
CPU-Target: 64Bit
Wohnort: Schweiz

Re: Datenspeicherung allgemein

Beitrag von Mathias »

Danke für die Aufklärung. Habe es mit begrenzten Strings versucht, also String[40], und es geht.

Das hätte ich auch so gemacht, für den Anfang, ist dies die einfachste Lösung.

Wen es professionell sein soll, dann würde ich TFileStream vorziehen.
Damit kann man Strings, beliebiger Länge und auch dynamische Array speichern.
Mit Lazarus sehe ich grün
Mit Java und C/C++ sehe ich rot

Timm Thaler
Beiträge: 1224
Registriert: So 20. Mär 2016, 22:14
OS, Lazarus, FPC: Win7-64bit Laz1.9.0 FPC3.1.1 für Win, RPi, AVR embedded
CPU-Target: Raspberry Pi 3

Re: Datenspeicherung allgemein

Beitrag von Timm Thaler »

Hint: Du solltest Dir überlegen, ob Du wirklich integer abspeichern willst. Die PLZ als String zu speichern benötigt zwar etwas mehr Platz - nee, wenn Dein integer ein 64bit-int ist sogar weniger, aber Deine Dateien sind im Editor lesbar.

Die Write/Read Funktionen für integer, real usw. stammen aus Zeiten, als man mit jedem Byte geizen musste. Das hat aber den Nachteil, dass Deine Dateien schwer lesbar sind, wenn Du sie mit einem anderen Programm öffnest. Heute kostet weder Speicherplatz noch die Wandlung von String zu Int oder Float nennenswert was. Deswegen Werte zu String wandeln und im Klartext abspeichern.

BernhardDEL
Beiträge: 36
Registriert: Di 31. Jan 2017, 17:18

Re: Datenspeicherung allgemein

Beitrag von BernhardDEL »

Mathias hat geschrieben:
Danke für die Aufklärung. Habe es mit begrenzten Strings versucht, also String[40], und es geht.

Das hätte ich auch so gemacht, für den Anfang, ist dies die einfachste Lösung.

Wen es professionell sein soll, dann würde ich TFileStream vorziehen.
Damit kann man Strings, beliebiger Länge und auch dynamische Array speichern.


Nachdem alles sich wunderbar compilieren läßt habe ich nun das Problems eines Laufzeitfehlers. Da die gesuchte Datei noch nicht vorhanden ist kann das Programm sie natürlich nicht finden.

Wie kann ich denn die Datei erstellen?
Habe es mit FileCreate versucht, aber dazu brauche ich ja ein Handel.

Das mit TFileStream würde ich ja machen, aber damit kenn ich mich nun garnicht aus.
(meine Erfahrungen mit Turbo Pascal liegen mehr als 25 Jahre zurück oder so. Da war noch nix mit "Objektorientiert" und/oder "Vererbung" - da war alles Ereignisgesteuert).

In den Büchern wird da auch nix zu gesagt. Der Autor des Lazarus-Buches verweist auf das nächste Buch (Teil 2), aber das ist noch garnicht auf dem Markt.
Zuletzt geändert von BernhardDEL am So 5. Feb 2017, 18:39, insgesamt 1-mal geändert.

BernhardDEL
Beiträge: 36
Registriert: Di 31. Jan 2017, 17:18

Re: Datenspeicherung allgemein

Beitrag von BernhardDEL »

Timm Thaler hat geschrieben:Hint: Du solltest Dir überlegen, ob Du wirklich integer abspeichern willst. Die PLZ als String zu speichern benötigt zwar etwas mehr Platz - nee, wenn Dein integer ein 64bit-int ist sogar weniger, aber Deine Dateien sind im Editor lesbar.


Du hast natürlich recht.
Bei einer Datei, die nur PLZ und Stadt beinhaltet spielt es keine Rolle, ob die im Editor verändert wird oder nicht.
Problematischer ist es da schon bei personenbezogenen Daten wie z.B. der Fahrer- oder Kundendatei.
Auch die Datei mit den Arbeitsblättern, die ja gegebenenfalls als rechtlicher Nachweis Bestand haben muss, darf nicht so einfach zu manipulieren sein.
Da muss ich mir eh später noch was einfallen lassen, von wegen Verschlüsselung oder so -- aber im Moment ist es wichtiger, dass ich meine Dateien erstellt bekomme, wenn sie noch nicht da sind :oops:

Timm Thaler
Beiträge: 1224
Registriert: So 20. Mär 2016, 22:14
OS, Lazarus, FPC: Win7-64bit Laz1.9.0 FPC3.1.1 für Win, RPi, AVR embedded
CPU-Target: Raspberry Pi 3

Re: Datenspeicherung allgemein

Beitrag von Timm Thaler »

Auch beliebt: Für Hausnummern int zu nehmen, und dann hat da jemand die 3b. Oder ausländische "Postleitzahlen" können durchaus mehr als 5 Stellen haben und Buchstaben enthalten. Oder ein Adressbuch, bei dem man in der Telefonnummer nur Ziffern eingeben kann: +33 als Ländervorwahl?

Schränke Dich im Datenformat so wenig wie möglich ein, erspart Dir hinterher viel Ärger.

BernhardDEL
Beiträge: 36
Registriert: Di 31. Jan 2017, 17:18

Re: Datenspeicherung allgemein

Beitrag von BernhardDEL »

Timm Thaler hat geschrieben:Auch beliebt: Für Hausnummern int zu nehmen, und dann hat da jemand die 3b. Oder ausländische "Postleitzahlen" können durchaus mehr als 5 Stellen haben und Buchstaben enthalten. Oder ein Adressbuch, bei dem man in der Telefonnummer nur Ziffern eingeben kann: +33 als Ländervorwahl?

Schränke Dich im Datenformat so wenig wie möglich ein, erspart Dir hinterher viel Ärger.


Timm, darum kann ich mich immer noch kümmern.
Jetzt ist es erst einmal wichtig, dass ich die Datei auch erstellt bekomme.
Die Länderkennung habe ich in meinem Programm eh separat festgehalten. Von daher kein Problem. Da wird dann auch die Ländervorwahl mit gespeichert.
Auch die Telefonnummer ist in Vorwahl und Nummer gesplittet.

Mein Problem liegt im Moment in der Speicherung der Daten in entsprechenden Dateien. :roll:
Zur Zeit weis ich auch noch nicht, wie ich meine Daten in die Items der Comboboxen bekomme. :?:

Wie gesagt sind meine Erfahrungen im Bezug auf Programmierung schon sehr alt (ich hinke der Entwicklung hinterher)
und mit 62 Jahren ist man auch nicht mehr so flexibel :cry: .

Jole
Beiträge: 114
Registriert: Fr 4. Jul 2014, 14:39
OS, Lazarus, FPC: Linux
CPU-Target: amd64

Re: Datenspeicherung allgemein

Beitrag von Jole »

BernhardDEL hat geschrieben: -- aber im Moment ist es wichtiger, dass ich meine Dateien erstellt bekomme, wenn sie noch nicht da sind

Wenn du das nicht mit TFileStream machen willst/kannst, dan könnte eine mögliche Lösung so aussehen.

Code: Alles auswählen

 
var PLZ_Stadt: file of Type_PLZ_Stadt;
 
// Dateinummer holen
AssignFile(PLZ_Stadt, 'Datei.ext');
// vor dem öffnen die Fehlerbehandlung selbst übernehmen
{$I-}
  Reset(PLZ_Stadt);
// Prüfen, ob ein Fehler Aufgetreten ist
If(IOResult <> 0) then // Wenn Fehler, dann
  ReWrite(PLZ_Stadt); // Datei erzeugen
{$I+} // Überprüfung wieder zurücksetzen
 

Ab jetzt steht dir die Datei zum lesen und schreiben zur verfühgung (schließen mit CloseFile(PLZ_Stadt).

Warf
Beiträge: 1908
Registriert: Di 23. Sep 2014, 17:46
OS, Lazarus, FPC: Win10 | Linux
CPU-Target: x86_64

Re: Datenspeicherung allgemein

Beitrag von Warf »

Timm Thaler hat geschrieben:Auch beliebt: Für Hausnummern int zu nehmen, und dann hat da jemand die 3b. Oder ausländische "Postleitzahlen" können durchaus mehr als 5 Stellen haben und Buchstaben enthalten. Oder ein Adressbuch, bei dem man in der Telefonnummer nur Ziffern eingeben kann: +33 als Ländervorwahl?

Schränke Dich im Datenformat so wenig wie möglich ein, erspart Dir hinterher viel Ärger.


Für so etwas hält man sich eigentlich an die nationale Norm. Grade in Deutschland ist das vom Staat explizit geregelt was wie erlaubt ist, und sollte sich da was Ändern so wird das sehr lange Zeit im Voraus angekündigt. Ich glaube sogar der Staat selbst gibt Definitionen für Unternehmen raus welche im Hinblick auf die zu verwendenden Datentypen alles genau definieren (Wie lang eine Hausnummer maximal sein darf, was für Zeichen vorkommen dürfen, etc.) Aber ich denke für den Fragensteller ist das wohl jetzt erst mal das geringste Problem.

BernhardDEL hat geschrieben:1. Jetzt ist es erst einmal wichtig, dass ich die Datei auch erstellt bekomme.
2. Zur Zeit weis ich auch noch nicht, wie ich meine Daten in die Items der Comboboxen bekomme. :?:


Zu 1. ich zitiere mein eigenes Zitat :wink: :
Mit dem AssignFile wird das Handle zugewiesen, danach muss die Datei allerdings noch geöffnet werden. Das öffnen geschieht über Reset, Rewrite oder Append.
Append ist zum Schreiben am Ende von Textdateien, also zum Anhängen von Texten. Rewrite erstellt eine neue Datei, exsistiert die Datei bereits wird sie überschrieben. Reset öffnet Textdateien zum lesen, oder Binärdateien zum Lesen und/oder Schreiben.


Zu 2. je nach Dateityp (Text, Typisiert, untypisiert) sind auch bei meinem Tutorial einige Beispiele, z.B. für Untypisierte Dateien zu lesen (also dateien in denen verschiedene Information verschiedener Typen reingeschrieben werden:

Code: Alles auswählen

 
   AssignFile(F, 'Test.bin');
   try
     FileMode := fmOpenRead; // Lesen
    Reset(F, 1); // 1 Byte RecordSize
    BlockRead(F, i, SizeOf(Integer)); // Integer nach Variable i Lesen
    BlockRead(F, s, SizeOf(s)); // ShortString nach Variable s Lesen
    BlockRead(F, d, SizeOf(d)); // Double nach Variable d Lesen
  finally
     CloseFile(F);
   end;


(Ich habe bei meinem ersten post leider einen kleinen Fehler da steht auch beim lesen überall schreiben, das habe ich hier korrigiert).

Und wie du Variablen in deine Comboboxen bekommst solltest du selbst wissen :wink:

Antworten