Step 07 - Arrays & Records

Spendenaufruf des DRK Ortsvereins Spielberg e.V.

16. April 2023

 

Hallo liebe Besucherin, hallo lieber Besucher!

In einem ganz anderen Kontext möchte ich hier nun bewerben:
Der Verein, für den ich auch einstehe, benötigt Spenden:


Bitte bei der Überweisung den Verwendungszweck beachten!

Der neuste Stand gibt es hier zu sehen!

Wer anderen hilft, hilft sich selbst. (Konfuzius)

 

 

 

Sammlung und Verarbeitung von Daten mit Arrays und Records

 
 
 
Auch wenn Arrays und Records zwei ganz unterschiedliche Themen sind, sind beide dennoch eine Erweiterung von Variablen und Konstanten, da Arrays und Records in gewisser Weise Sammlung von Daten darstellt. Arrays ist die Sammlung von Daten des gleichen Datentypes, wobei Records eher eine Art Objekt darstellt, das verschiedene Datentypen enthalten kann.

Kommentar des Webmasters:

Dieses Tutorial hat Nicolai B. in Eigenregie vorbereitet, programmiert und damals in der AG vorgetragen. Er hat hier das Thema File-Handling, was erst später im Fortgeschrittenen-Bereich näher gezeigt wird, vorausgenommen. Wer hierzu mehr erfahren möchte, kann gerne hier weiterlesen.
Vielen Dank an Nicolai B. für diesen Beitrag!
Auch wenn ich den Beitrag gerne unverändert im neuen Design der Website übernommen hätte, ist er dennoch nicht mehr ganz Zeitgemäß, weshalb ich ihn etwas überarbeitet habe. Und nun, wie Spaß mit Arrays und Records :)
 

Arrays:

Ein Array ist eine statische Liste, welche Werte eines vorbestimmten Types enthalten kann. Das ganze stellt man sich am besten so vor: Man hat eine Straße (=Array) mit gleichen Häusern (=Typ), aber anderen Familien (=Werte) und natürlich anderen Hausnummern. Die Hausnummern bilden die Indizes, über welche man auf die Werte zugreifen kann. Der Index eines Arrays beginnt normalerweise bei "0" und wird in eckigen Klammer dargestellt:
myarray[0];
Name des arrays[Index des Arrays];
Einem Array wird ein Wert, wie bei einer Variable, zugewiesen.
Es gibt zwei verschiedene Arten von Arrays, statische Arrays und dynamische Arrays.
 
Ein statisches Array ist ein Array mit einer bestimmten Größe. Es wird wie folgt deklariert:
var myArray : Array[0..4] of Interger ;
Name des Arrays ; Größe des Arrays ; Typ des Arrays
Die Größe kann bei diesem Beispiel während der Laufzeit nicht verändert werden.
Initialisiert wird das Array dann beispielsweise so:
  myArray[ 0 ] := 0;
  myArray[ 1 ] := 0;
  myArray[ 2 ] := 0;
  myArray[ 3 ] := 0;
  myArray[ 4 ] := 0;
 
Dynamische Arrays werden wie die statischen Arrays nur ohne die Größenangabe deklariert:
var myArray : Array of Interger ;
Name des Arrays ; Typ des Arrays
Die Größe eines dynamischen Array kann man während der Laufzeit ändern:
SetLength( myarray , 10 );
Name des Arrays ; Neue größe des Arrays
Initialisiert wird das dynamische genauso wie das statische Array.
Beispiele siehe später bei Step 11 (For-Schleife).
 

Records:

Records sind eine Art Verbund von mehreren Elementen.
Diese Elemente, die Variablen nicht unähnlich sind, können von verschiedenen Datentypen sein.
Hier die Deklaration und Initialisierung:
procedure TMainForm.BtnDemoClick(Sender:TObject);
type
  TAdresse = record
    Vorname: String;
    Nachname: String;
    Strasse: String;
    Hausnmr: Integer;
    Ort: String;
    PLZ: String;
  end; // of Record

var
  tmpAdr : TAdresse;
  AdressenArray : Array[0..4] of TAdresse;
  // Beinhaltet insgesamt 5 Adressen!

begin
  // Initialisierung des Adressen-Arrays
  AdressenArray[0].Vorname:='Klaus';
  AdressenArray[0].Nachname:='Walter';
  // ...

  AdressenArray[4].Ort:='Klaustal';
  AdressenArray[4].PLZ:=54321;
end;
Die "Typdeklaration" besteht aus dem Schlüsselwort "type" und anschließend fast genauso, wie wir es bei der Variablendeklaration kennengelernt haben.
(Wichtig ist "=" anstatt ":" und "end;" anstatt nur ";"!)
Der Vorteil hierbei ist, dass man mehrere "Instanzen" von diesem Typ bilden kann, wie man es mit Variablen bereits gewohnt ist.
Nun haben wir den Typus "Record" bereits kennengelernt. Am Ende im Abschnitt OOP werden wir eine weitere Art von "type" kennenlernen.
 

Demo-Anwendung:

Hier eine fortgeschrittene Demo, was man mit dem "Type" Record und dem Array schönes machen kann:
Um die Demo zu bauen, braucht ihr auf dem Formular:
  • 4 Buttons
  • 16 Edits
  • 16 Labels
Den Source-Code könnt ihr dann entsprechend ergänzen - und auch erweitern. Überall an den "..."-Stellen sollen entsprechend die weiteren Zeilen für die anderen Edits stehen - hier ist das jetzt stark verkürzt um die Seite nicht zu lang und unübersichtlich zu gestalten.
// das hier in den Interface-Bereich direkt bei type
type
  TICQKontakt = record
    Nick : String[10]; // die Zahl in den Eckigen Klammern gibt
    Vorname : String[25]; // die maximale Größe des "Strings" an!
    Nachname : String[25];
    UIN : Integer;
  end;

  T4ICQKontaktBuch = Array[0..3] of TICQKontakt;




// das hier in den privat-Abschnit des TMainForms, direkt unter
                                        { Private-Deklarationen }
  ICQKontaktBuch : T4ICQKontaktBuch;



// das hier in die Click-Procedure des Buttons,
// der die Eingaben übernehmen soll:
begin
  ICQKontaktBuch[0].Nick     := EdNick1.Text;
  ICQKontaktBuch[0].Vorname  := EdVorname1.Text;

  // ... copy & paste ;-)

  ICQKontaktBuch[3].Nachname := EdNachname4.Text;
  ICQKontaktBuch[3].UIN      := StrToInt( EdIcqNummer4.Text);
end;



// das hier in die Click-Procedure des Buttons,
// der die geladene Eingaben anzeigen soll:
begin
  EdNick1.Text      := ICQKontaktBuch[0].Nick;
  EdVorname1.Text   := ICQKontaktBuch[0].Vorname;

  // ... copy & paste ;-)

  EdNachname4.Text  := ICQKontaktBuch[3].Nachname;
  EdIcqNummer4.Text := IntToStr( ICQKontaktBuch[3].UIN);
end;



// das hier in die Click-Procedure des Buttons,
// der alles speichern soll:

var
  f : file of T4ICQKontaktBuch;
begin
  // Dateihandling ist neu - nehmt euch Zeit um euch das kurz anzuschauen

  // wir definieren hier quasi erst einen sogenannten "Zeiger" f
  // auf die Datei "demofile.dat" um mit diesem weiter zu arbeiten
  // Damit die Datei im gleichen Verzeichnis landet, wie unsere Exe-Datei
  // ist hier noch ExtractFilePath als Hilfsfunktion dabei.
  AssignFile(f, ExtractFilePath(Application.Exename) + 'demofile.dat');

  // da wir in diesem Fall "neu" speichern wollen, nutzen wir Rewrite() um
  // die Datei zu leeren
  ReWrite(f);

  // nun schreiben wir unser Kontaktbuch in die Datei
  Write(f,ICQKontaktBuch);

  // und zu guter Letzt, schließen wir den Zeiger auf die Datei, so dass ggf.
  // jemand Anderes darauf zugreifen kann :)
  CloseFile(f);
end;



// das hier in die Click-Procedure des Buttons,
// der alles laden soll:

var
  f : file of T4ICQKontaktBuch;

begin
  // auch hier definieren wir erst den Zeiger auf die Datei
  AssignFile(f, ExtractFilePath(Application.Exename) + 'demofile.dat');

  // nun stellen wir sicher, dass der Zeiger auf den Anfang zeigt
  ReSet(f);

  // Und jetzt lesen wir unser Kontaktbuch aus der Datei
  Read(f,ICQKontaktBuch);

  // Wie oben, alles wieder schließen
  CloseFile(f);
end;
Zum Testen könnt ihr einfach das Programm starten, dann die Felder ausfüllen, auf Übernehmen und Speichern klicken, und nach dem Neustart sollten über Laden und Anzeigen die Werte wieder erscheinen :)
 
Auch wenn ICQ nicht mehr so wie früher existiert, dient die UIN heutzutage immer noch prima als Beispiel, wenn man eine Nummer abspeichern möchte. ICQ war ein Messenger, der leider nie ausgebaut wurde. Heutzutage haben die Betreiber von Mail.ru den Dienst übernommen und damit einen Start in die Neuzeit gewagt, indem sie ihn prinzipiell mit ähnlichen Funktionen wie Whatsapp oder Telegramm ausgestattet haben.
Preisfrage: Wer erkennt hieran, dass das so lange nicht weitergehen kann, wenn man z.B. 300 Kontakte speichern möchte? Eine Hinweis für eine dynamischere Lösung kann Step 11 bringen und bei den Vertiefenderen-Tutorials gehe ich unter Filehandling noch mehr darauf ein ;)
Den kompletten Source-Code für dieses Projekt findet ihr unter GitHub.
 

Neu seit 10.4: Managed Records:

Managed Records sind eine neue Funktion, die in Delphi 10.4 Sydney eingeführt wurde. Sie ermöglichen es, benutzerdefinierte Initialisierungs- und Finalisierungsoperatoren für Records zu definieren.
Hier sind zwei Beispiele:
type
  TMyRecord = record
    Value: Integer;
    class operator Initialize(var ARecord: TMyRecord);
  end;

class operator TMyRecord.Initialize(var ARecord: TMyRecord);
begin
  ARecord.Value := 0;
end;
In diesem Beispiel wird der Wert des Feldes "Value" auf 0 gesetzt, wenn eine neue Instanz von TMyRecord erstellt wird.
Beispiel Nummer Zwei:
type
  TMyRecord = record
    Value: PChar;
    class operator Finalize(var ARecord: TMyRecord);
  end;

class operator TMyRecord.Finalize(var ARecord: TMyRecord);
begin
  if ARecord.Value <> nil then
    FreeMem(ARecord.Value);
end;
In diesem Beispiel wird der Speicher freigegeben, der von ARecord.Value allokiert wurde, wenn eine Instanz von TMyRecord zerstört wird.
Managed Records können auch mit Arrays verwendet werden, um sicherzustellen, dass die Elemente des Arrays ordnungsgemäß initialisiert und freigegeben werden.
procedure TMainForm.BtnTestMgmtRecordClick(Sender:TObject);
type
  TScrumValue = record
    Value: string;
    class operator Initialize(out Dest: TScrumValue);
    class operator Finalize(var Dest: TScrumValue);
  end;

class operator TScrumValue.Initialize(out Dest: TScrumValue);
begin
  Dest.Value := 'not set';
end;

class operator TScrumValue.Finalize(var Dest: TScrumValue);
begin
  if Dest.Value <> nil then
    FreeMem(Dest.Value);
end;

var
  ScrumValues: array[0..4] of TScrumValue;
begin
  // ...
  ScrumValues[0].Value := 'Commitment';
  ScrumValues[1].Value := 'Mut';
  ScrumValues[2].Value := 'Respekt';
  ScrumValues[3].Value := 'Fokus';
  ScrumValues[4].Value := 'Offenheit';
  // ...
end;