Record zu Array

Für Fragen zur Programmiersprache auf welcher Lazarus aufbaut
Antworten
Ronny58
Beiträge: 90
Registriert: So 27. Apr 2014, 20:35

Record zu Array

Beitrag von Ronny58 »

Vielleicht ein dumme Frage, aber im Moment beschäftigt sie mich.

Gibt es eine elegante Methode, um einen Record in ein Array zu übertragen?

Beispiel:

Code: Alles auswählen

TDaten = record
  Name : String;
  Vorname: String
  ... : String;
Im Moment fällt mir nur ein:

Code: Alles auswählen

   Array[0] := Daten.Name;
   Array[1] := Daten.Vorname;
   ...
Bei größeren Records viel Code.

Ich könnte den Record natürlich auch gleich als ein Array anlegen. Schon klar. Das wird bei größeren Records aber schwerer lesbar, wenn man nur über einen Index auf ein bestimmtes Feld zugreift. Da kann man sich bei Änderungen leicht mal verhaspeln. Auch wenn man sich für jedes Feld im Record eine sprechende Index-Konstante definiert. Z.B. iName = 0, iVorname = 1.

Toll wäre auf die definierten Felder eines Records in einer Schleife zugreifen zu können, ohne den Record selbst zu verwurschteln.

geht das?

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

Re: Record zu Array

Beitrag von theo »

Vllt. verstehe ich dich falsch, aber warum machst du nicht aus dem Record ein Array?

Code: Alles auswählen

type
  TDaten = record
    Name : String;
    Vorname: String
  end;

TDatenArray = Array [0..10] of TDaten;     

....

var MyDatenArray:TDatenArray;
begin
    MyDatenArray[0].Name:='test';
    MyDatenArray[0].Vorname:='test';
end;  

wp_xyz
Beiträge: 4869
Registriert: Fr 8. Apr 2011, 09:01

Re: Record zu Array

Beitrag von wp_xyz »

Es geht wahrscheinlich mit einem Typecast von dem Record mit Strings auf das Array mit Strings, evtl unter Zuhilfenahme der entsprechenden Zeigertypen.

Schöner und sauberer ist es auf jeden Fall stattdessen einen Type-Helper für deinen Record zu verwenden:

Code: Alles auswählen

type
  TDaten = record
    Name: String;
    Vorname: String;
    Ort: String;
  end;

  TDatenHelper = Record Helper for TDaten
    function ToStringArray: TStringArray;
  end; 
    
function TDatenHelper.ToStringArray: TStringArray;
begin
  SetLength(Result, 3);
  Result[0] := Name;
  Result[1] := Vorname;
  Result[2] := Ort;
end;  
Zugriff:

Code: Alles auswählen

procedure TForm1.FormCreate(Sender: TObject);
var
  i: Integer;
  s: String;
begin
  SetLength(FDaten, 2);
  FDaten[0].Name := 'Einstein';
  FDaten[0].Vorname := 'Albert';
  FDaten[0].Ort := 'Ulm';

  FDaten[1].Name := 'Newton';
  FDaten[1].Vorname := 'Isaac';
  FDaten[1].Ort := 'Woolsthorpe-by-Colsterworth';

  for i := 0 to High(FDaten) do
  begin
    s := Format('%s, %s (%s)', [
      FDaten[i].ToStringArray[0], FDaten[i].ToStringArray[1], FDaten[i].ToStringArray[2]
    ]);
    Listbox1.Items.Add(s);
  end;
end;
Achtung: Im Compiler-Mode {$mode objfpc} muss noch ein {$modeswitch AdvancedRecords} an den Kopf der Unit.

Ronny58
Beiträge: 90
Registriert: So 27. Apr 2014, 20:35

Re: Record zu Array

Beitrag von Ronny58 »

schön und sauber ist toll.
Das verstehe ich dann auch in einem Jahr auch wieder, wenn ich was ändern muss.
Das nehme ich.

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

Re: Record zu Array

Beitrag von Mathias »

Ich habe noch ein anderen Lösungsansatz:

Code: Alles auswählen

{$mode objfpc}

type
  TDaten = record
    case integer of
      1: (Name, Vorname, Ort: string);
      2: (Adresse: array[0..2] of string);
  end;

var
  Daten: array of TDaten;
  i: integer;

begin
  SetLength(Daten, 2);
  Daten[0].Name := 'Mueller';
  Daten[0].Vorname := 'Hans';
  Daten[0].Ort := 'Luzern';

  Daten[1].Name := 'Meier';
  Daten[1].Vorname := 'Ueli';
  Daten[1].Ort := 'Bern';

// Ausgabe:
  for i := 0 to Length(Daten) - 1 do begin
    WriteLn(Daten[i].Adresse[0]: 10, Daten[i].Adresse[1]: 10, Daten[i].Adresse[2]: 10);
  end;
end.   
Mit Lazarus sehe ich grün
Mit Java und C/C++ sehe ich rot

wp_xyz
Beiträge: 4869
Registriert: Fr 8. Apr 2011, 09:01

Re: Record zu Array

Beitrag von wp_xyz »

Strings sind kompliziert...

Das funktioniert nur, weil du das {$H+} nach dem {$MODE ObjFPC} weggelassen hast. Damit arbeitest du mit ShortStrings (max Länge 255) statt mit "normalen" Strings, die in beliebiger Länge auf dem Heap abgelegt sind. Mit dem {$H+} erhältst du diese Fehlermeldung:
project1.lpr(6,37) Error: Data types which require initialization/finalization cannot be used in variant records

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

Re: Record zu Array

Beitrag von Mathias »

Damit arbeitest du mit ShortStrings (max Länge 255
Stimmt. hast recht

Code: Alles auswählen

WriteLn(SizeOf(Daten[1].Name)); 
spuckt 256 aus. :oops:

Auch mit dynamischen Arrays geht es nicht.

Code: Alles auswählen

  Ta = array of char;

  TDaten2 = record
    case integer of
      1: (Name, Vorname, Ort: Ta);
      2: (Adresse: array[0..2] of Ta);
  end; 
Aber wieso geht es mit einem PChar oder einem Pointer ?
Ein Ansistring oder ein dynamisches ist doch auch nur ein Pointer oder ?
Mit Lazarus sehe ich grün
Mit Java und C/C++ sehe ich rot

mschnell
Beiträge: 3444
Registriert: Mo 11. Sep 2006, 10:24
OS, Lazarus, FPC: svn (Window32, Linux x64, Linux ARM (QNAP) (cross+nativ)
CPU-Target: X32 / X64 / ARMv5
Wohnort: Krefeld

Re: Record zu Array

Beitrag von mschnell »

Nur eine ungetestete Idee: Kann RTTI die Record Elemente enumerieren ?
-Michael

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

Re: Record zu Array

Beitrag von Warf »

Auch eine Option, über pointer:

Code: Alles auswählen

program Project1;

{$mode objfpc}{$H+}

generic function RecordToArray<TRecType, TDataType>(constref rec: TRecType): specialize TArray<TDataType>;
type
  PDataType = ^TDataType;
const
  numElements: SizeInt = SizeOf(TRecType) div SizeOf(PDataType);
var
  i: SizeInt;
  recPointer: PDataType;
begin
  recPointer := PDataType(@rec);
  Result := nil;
  SetLength(Result, numElements);
  for i:= 0 to numElements - 1 do
    Result[i] := recPointer[i];
end;

type
  TTestRec = record
    A, B, C: String;
  end;

var
  t: TTestRec;
  a: specialize TArray<String>;
  s: String;
begin
  t.A := 'A';
  t.B := 'B';
  t.C := 'C';
  a := specialize RecordToArray<TTestRec, String>(t);
  for s in a do
    WriteLn(s);
  Readln;
end.
Aber die Frage ist eher, warum willst du einen neuen array erstellen, und alle felder rüber kopieren, statt einfach den record zu nehmen, auf die kannst du auch wie arrays zugreifen:

Code: Alles auswählen

var
  p: PString;
begin
  p := PString(@ARecord);
  p[0] := 'Foo';
  p[1] := 'Bar';
end;

PascalDragon
Beiträge: 825
Registriert: Mi 3. Jun 2020, 07:18
OS, Lazarus, FPC: L 2.0.8, FPC Trunk, OS Win/Linux
CPU-Target: Aarch64 bis Z80 ;)
Wohnort: München

Re: Record zu Array

Beitrag von PascalDragon »

Mathias hat geschrieben:
Do 3. Sep 2020, 15:06
Ein Ansistring oder ein dynamisches ist doch auch nur ein Pointer oder ?
Ja, in dem Record sind sie nur Pointer, aber AnsiStrings und dynamische Arrays sind verwaltete Typen, das heißt der Compiler ruft spezielle Funktionen auf, wenn sie zugewiesen oder freigegeben werden. Nimm mal an, dass du folgendes Record hast:

Code: Alles auswählen

type
  TMyRecord = record
    case Boolean of
      True: (S: AnsiString);
      False: (A: array of LongInt);
  end;
Beide Felder hätten den gleichen Offset im Record. Wenn der Compiler nun das Record zu einem anderen zuweist oder das Record freigibt, ruft er nun die Verwaltungsfunktionen des Arrays oder des Strings auf? Der Compiler hat schließlich keine Ahnung was in dem Record jetzt von den beiden gespeichert ist.
mschnell hat geschrieben:
Do 3. Sep 2020, 15:19
Nur eine ungetestete Idee: Kann RTTI die Record Elemente enumerieren ?
-Michael
Ja, das geht. Ein Beispiel dazu gibt's im Englischen Forum.
FPC Compiler Entwickler

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

Re: Record zu Array

Beitrag von Mathias »

Mit dem LongInt hast du noch eine Falle, besser PtrInt.
Mit Lazarus sehe ich grün
Mit Java und C/C++ sehe ich rot

mschnell
Beiträge: 3444
Registriert: Mo 11. Sep 2006, 10:24
OS, Lazarus, FPC: svn (Window32, Linux x64, Linux ARM (QNAP) (cross+nativ)
CPU-Target: X32 / X64 / ARMv5
Wohnort: Krefeld

Re: Record zu Array

Beitrag von mschnell »

PascalDragon hat geschrieben:
Fr 4. Sep 2020, 09:14
mschnell hat geschrieben:
Do 3. Sep 2020, 15:19
Nur eine ungetestete Idee: Kann RTTI die Record Elemente enumerieren ?
-Michael
Ja, das geht. Ein Beispiel dazu gibt's im Englischen Forum.
Das würde die Möglichkeit einer sauberen (aber langsamen)Abbildung eines Aecords auf ein Array erlauben.
-Michael

PascalDragon
Beiträge: 825
Registriert: Mi 3. Jun 2020, 07:18
OS, Lazarus, FPC: L 2.0.8, FPC Trunk, OS Win/Linux
CPU-Target: Aarch64 bis Z80 ;)
Wohnort: München

Re: Record zu Array

Beitrag von PascalDragon »

Mathias hat geschrieben:
Fr 4. Sep 2020, 09:23
Mit dem LongInt hast du noch eine Falle, besser PtrInt.
Es kommt halt drauf an, was du letztendlich haben willst. Wenn du mit 32-bit Werten arbeitest, dann ist PtrInt fehl am Platz. Und für mein Beispiel war das vollkommen egal, das hätte auch ein array of Boolean sein können.
FPC Compiler Entwickler

Antworten