Records aus einem Byte Array herauslesen?

Für Fragen zur Programmiersprache auf welcher Lazarus aufbaut
Helios
Lazarusforum e. V.
Beiträge: 106
Registriert: Mi 29. Jun 2011, 22:36
OS, Lazarus, FPC: Lazarus 2.2.4 Windows 10 64Bit / Lazarus 2.0.12 Debian 11.7 „Bullseye" 64Bit
CPU-Target: 64Bit
Wohnort: Leonberg

Records aus einem Byte Array herauslesen?

Beitrag von Helios »

Hallo Forenmitglieder,
nach langer Suche im web/Google ohne konkrete Lösung bitte ich hier im Forum um einen Tipp.
Ich suche für folgendes Problem eine elegante Lösung.

Aus einer Datei lese ich via Blockread Byte Arrays heraus, die ich erst zur Laufzeit konktet interpretieren kann

Zum Beispiel kann es sich um ein Array Of TableFuncParam handeln:

Type
TableFuncParam = packed record
Value1: double;
ByteArray: array [0..31] of byte;
end;

Wie gesagt, aus der Datei hole ich mir immer ein Array Of Bytes.

var
TFPArray: Array Of TableFuncParam;
ByteArray: Array Of Bytes;

Nun möchte ich den Byte Array Speicherbereich mit meinem Record Array "zur Deckung" bringen, z.B. mit so einem Konstrukt:
...
TableFuncParam[0] := TableFuncParam(ByteArray[0]);
...

Gibt es eine Lösung mit einem Casting (packed Rekord Array "passt exakt" in das Byte Array)
oder muss ich wirklich Byte-weise die Information vom Byte Array in ein entsptechend neues
TableFuncParam Array kopieren.
Ich hatte auch schon Beispiele mit "absolute" gefunden, bekomme es aber mit Record Arrays nicht zum fliegen.

Im Voraus besten Dank für Eure Rückmeldungen.

Gruß

Arne

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

Re: Records aus einem Byte Array herauslesen?

Beitrag von Mathias »

Vielleicht hilft dir "absolute" weiter.
Mit Lazarus sehe ich grün
Mit Java und C/C++ sehe ich rot

Helios
Lazarusforum e. V.
Beiträge: 106
Registriert: Mi 29. Jun 2011, 22:36
OS, Lazarus, FPC: Lazarus 2.2.4 Windows 10 64Bit / Lazarus 2.0.12 Debian 11.7 „Bullseye" 64Bit
CPU-Target: 64Bit
Wohnort: Leonberg

Re: Records aus einem Byte Array herauslesen?

Beitrag von Helios »

Hallo Matthias,

wie beschrieben, "absolute" hatte ich schon versucht, aber leider fehlt mir hier ein "griffiges" Beispiel für Arrays Of Records.

Wenn Du diesbzgl. etwas (einen Link oder ein gutes Beispiel) hast, wäre ich Dir sehr dankbar.

Gruß

Arne

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

Re: Records aus einem Byte Array herauslesen?

Beitrag von Mathias »

Wieso liest du deine Datei nicht mit TStream ein ?
Die Daten von einem Stream kannst du sehr einfach in deinen Record schreiben.
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: Records aus einem Byte Array herauslesen?

Beitrag von wp_xyz »

Ich verstehe zwar nicht, warum du nicht gleich in eine Variable des richtigen Typs (TableFuncParam) einliest, aber das wird schon seinen Grund haben.

Wenn du zuerst in ein Byte-Array ("b") einlesen musst, und der Record mit dem Typ TableFuncParam steht ab Index "i", dann gibt es folgende Möglichkeiten (wahrscheinlich noch mehr):

  • Kopieren per Move:

    Code: Alles auswählen

    var
      tfp: TableFuncParam;
    ...
      Move(b[i], tfp, SizeOf(tfp));
     
  • Type-cast des Pointers

    Code: Alles auswählen

    type
      PTableFuncParam = ^TableFuncParam;
    var
      tfp: TableFuncParam;
    ..
      tfp := PTablefuncParam(@b[i])^;
     
    // evtl geht auch:
      tfp := TableFuncParam(@b[i]^);

Bei dir ist tfp keine einzelne Variable, sondern Element eines Array of TableFuncParam. In diesem Fall ersetze im obigen Fall tfp durch das entsprechende Array-Element tfp[index].

Helios
Lazarusforum e. V.
Beiträge: 106
Registriert: Mi 29. Jun 2011, 22:36
OS, Lazarus, FPC: Lazarus 2.2.4 Windows 10 64Bit / Lazarus 2.0.12 Debian 11.7 „Bullseye" 64Bit
CPU-Target: 64Bit
Wohnort: Leonberg

Re: Records aus einem Byte Array herauslesen?

Beitrag von Helios »

Hallo wp_xyz
hallo Mathias,

besten Dank für Eure Rückmeldungen.
Kurz zu Eurer Frage: Erst zur Laufzeit weiss ich, wie der Byte Stream zu interpretieren ist.
Beim ersten lesen/parsen der Datei (MDF = Measurement Data Format von Vector) lege ich
daher die Daten "neutral" als Array Of Byte ab (habe Sie aus der Datei mit ByteRead ausgelesen
klappte bisher alles vorzüglich) um Sie später dann ohne erneuten Datei Lesezugriff die
Daten in den verschiedenen Record (Arrays) zu kopieren.

@wp_xyz:
Der Ansatz
...
tfp[j] := PTablefuncParam(@b[i])^;
...
compiliert sauber, nur funktioniert der Zugriff auf tfp[i] elemente nicht über z.B. low(tfp) bzw. high(tfp).
Darüber bekomme ich statt dessen die Werte low(b) = 0 und high(b) = 79 (in dem Byte Array sind 80 Bytes die passen für 2 ftp Records),
Ich glaube ich bin mit Eurer Hilfe nahe dran und werde in den nächsten Tagen diesbgl. weiterforschen.
Entsprechendes Ergebnis melde ich hier dann zurück.
Danke nochmal für Eure Unterstützung und Gruß
Arne

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

Re: Records aus einem Byte Array herauslesen?

Beitrag von Warf »

Code: Alles auswählen

var
TFPArray: Array Of TableFuncParam;
ByteArray: Array Of Bytes;


Das sind dynamische Arrays, die kannst du einfach casten:

Code: Alles auswählen

TBytes(TFPArray) := ByteArray

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

Re: Records aus einem Byte Array herauslesen?

Beitrag von wp_xyz »

Helios hat geschrieben:@wp_xyz:
Der Ansatz
...
tfp[j] := PTablefuncParam(@b[i])^;
...
compiliert sauber, nur funktioniert der Zugriff auf tfp[i] elemente nicht über z.B. low(tfp) bzw. high(tfp).
Darüber bekomme ich statt dessen die Werte low(b) = 0 und high(b) = 79 (in dem Byte Array sind 80 Bytes die passen für 2 ftp Records),

i und j sind natürlich unterschiedliche Indizes. i läuft in dem Byte-Array b - jedes Byte ein neuer Index. j dagegen durchläuft das TableFuncParam-Array. Wenn die TableFuncParam Elemente unmittelbar hintereinander im Byte-Array stecken, dann wird j immer dann erhöht, wenn i um 40 Byte (= SizeOf(TablefuncParam)) angewachsen ist. Schau dir mal folgenden (ungetesteten) Code an

Code: Alles auswählen

var
  i, j: Integer;
  b: array of byte;
  tfp: array of TableFuncParam;
begin
  SetLength(tfp, Length(b) div SizeOf(TableFuncParam));
  i := 0;
  for j := Low(ffp) to High(tfp) do begin
    tfp[j] := PTableFuncParam(@b[i])^;
    inc(i, Sizeof(TableFuncParam));
  end;
end;

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

Re: Records aus einem Byte Array herauslesen?

Beitrag von Mathias »

Erst zur Laufzeit weiss ich, wie der Byte Stream zu interpretieren ist.

Du kannst problemlos, zur Laufzeit sagen, was und wo du etwas aus den Strem holen willst.
In diesem Beispiel öffne ich die project1.lpr, und hole den Record, dort wo das Wort "Project1" steht.

Code: Alles auswählen

program Project1;     
uses
  Classes;
 
var
  fs: TFileStream;
  r: record
    a, b, c, d, e, f, g, h: char;
  end;
 
begin
  fs := TFileStream.Create('project1.lpr', fmOpenReadWrite);
  fs.Position := 8;
  fs.Read(r, 8);
 
  with r do begin
    WriteLn(a, b, c, d, e, f, g, h);
  end;
 
  fs.Free;
end.

Ich verwende nur Pointer und Zeiger, wen es nicht anders geht.
Mit Lazarus sehe ich grün
Mit Java und C/C++ sehe ich rot

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

Re: Records aus einem Byte Array herauslesen?

Beitrag von Warf »

wp_xyz hat geschrieben:i und j sind natürlich unterschiedliche Indizes. i läuft in dem Byte-Array b - jedes Byte ein neuer Index. j dagegen durchläuft das TableFuncParam-Array. Wenn die TableFuncParam Elemente unmittelbar hintereinander im Byte-Array stecken, dann wird j immer dann erhöht, wenn i um 40 Byte (= SizeOf(TablefuncParam)) angewachsen ist. Schau dir mal folgenden (ungetesteten) Code an

Code: Alles auswählen

var
  i, j: Integer;
  b: array of byte;
  tfp: array of TableFuncParam;
begin
  SetLength(tfp, Length(b) div SizeOf(TableFuncParam));
  i := 0;
  for j := Low(ffp) to High(tfp) do begin
    tfp[j] := PTableFuncParam(@b[i])^;
    inc(i, Sizeof(TableFuncParam));
  end;
end;


Kopieren geht auch einfach

Code: Alles auswählen

type
  TIrgendwasArray: array of TIrgendwas;
var
  bytes: TBytes;
  IwasArray: TIrgendwasArray;
begin
  IwasArray := TIrgendwasArray(bytes);
  SetLength(IwasArray, Length(IwasArray));
 

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

Re: Records aus einem Byte Array herauslesen?

Beitrag von wp_xyz »

Warf hat geschrieben:Kopieren geht auch einfach

Code: Alles auswählen

type
  TIrgendwasArray: array of TIrgendwas;
var
  bytes: TBytes;
  IwasArray: TIrgendwasArray;
begin
  IwasArray := TIrgendwasArray(bytes);
  SetLength(IwasArray, Length(IwasArray));
 

Da steig ich nicht durch. Was soll es bringen, wenn ich die Länge eines Arrays auf den Wert setze, den es schon hat?

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

Re: Records aus einem Byte Array herauslesen?

Beitrag von Warf »

Bei dem Cast wird nur der Arraypointer übergeben, und der RefCount erhöt.
Setlength garantiert das das ergebnis die RefCount 1 hat. Da die RefCount aber größer 1 ist, wird der Speicher kopiert, und ein neues Array mit RefCount 1 erzeugt

Helios
Lazarusforum e. V.
Beiträge: 106
Registriert: Mi 29. Jun 2011, 22:36
OS, Lazarus, FPC: Lazarus 2.2.4 Windows 10 64Bit / Lazarus 2.0.12 Debian 11.7 „Bullseye" 64Bit
CPU-Target: 64Bit
Wohnort: Leonberg

Re: Records aus einem Byte Array herauslesen?

Beitrag von Helios »

Hallo Gentlemen,

vielen Dank für Euren super Support am Sonntag Abend!

Hier noch der Ausschnitt aus meinem Source welches ich mit Eurer Hilfe erstellen konnte
(wobei ich das zusätzlich schöne inc() von wp_xyz als Ersatz für meine aufwändigere (Index) Multiplikation gerne verwenden/übernehmen werde.
Wieder was gelernt!:-)

type
...
ASAM_MCD2_Text_TableFuncParam = packed record // type 11
Value1: double;
ByteArray: array [0..31] of byte;
end;
...
PASAM_MCD2_Text_TableFuncParam = ^ASAM_MCD2_Text_TableFuncParam;

TFunc = class
public
...
TableValues: array of ASAM_MCD2_Text_TableFuncParam;
...
end;

constructor TFunc.Create(param: array of byte; size: word);
var
i: word;
begin
SetLength(TableValues, size);

for i := low(TableValues) to high(TableValues) do
TableValues[i] := PASAM_MCD2_Text_TableFuncParam(@param[i*SizeOf(ASAM_MCD2_Text_TableFuncParam)])^;
end;

Nochmals Danke!
Falls jemand noch eine Lösung hat, bei dem gar keine Iteration über die TableValues[i]
(und kein Aufruf von SetLength?) notwendig ist (evtl. der Ansatz von Warf?)
Wäre ich daran auch sehr interessiert, aber schon so ist das schon deutlich schöner als das was ich vorher
zusammenprogramiert habe.
Gute Nacht und Euch allen einen schönen Start in die Woche!
Gruß

Arne

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

Re: Records aus einem Byte Array herauslesen?

Beitrag von Mathias »

Falls jemand noch eine Lösung hat, bei dem gar keine Iteration über die TableValues[i]

Was haltet dich vom TFileStream ab ?
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: Records aus einem Byte Array herauslesen?

Beitrag von wp_xyz »

Warf hat geschrieben:Bei dem Cast wird nur der Arraypointer übergeben, und der RefCount erhöt.
Setlength garantiert das das ergebnis die RefCount 1 hat. Da die RefCount aber größer 1 ist, wird der Speicher kopiert, und ein neues Array mit RefCount 1 erzeugt


Schön zu wissen. Aber ehrlich gesagt - vor solchem Code halte ich Abstand, da verkommt die schön leserliche Programmiersprache Pascal zur Geheimschrift a la C & Konsorten.

Antworten