Records aus einem Byte Array herauslesen?

Für Fragen zur Programmiersprache auf welcher Lazarus aufbaut
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: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.


Naja ich finde es deutlich übersichtlicher als Pointerarithmetik. Man muss nunmal das verhalten von SetLength kennen (zum einen finde ich das verhalten weder unerwartet, noch dazu finde ich sollte man erwarten können das Pascalprogrammierer wenigstens die grundlegenden System Funktionen kennen sollten, und falls nicht, steht es in der Doc), oder einen Kommentar drüber schreiben. Das tolle an SetLength ist auch, wenn es nichts kopieren muss, kopiert es auch nichts. Und wenn man eine Zeile sowas wie "ensures a unique copy" als Kommentar drüber schreibt, kann jeder mit einem blick sehen was passiert. Bis man die Pointerschleife verstanden hat braucht man da länger

Helios hat geschrieben: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


Verwende doch Move, genau dafür existiert die Funktion doch:

Code: Alles auswählen

SetLength(TableArray, Length(ByteArray) div SizeOf(ASAM_MCD2_Text_TableFuncParam));
Move(ByteArray[0], TableArray[0], Length(ByteArray);

Ist zwar etwas länger als SetLength, aber immer noch deutlich kürzer als dein Code und deutlich übersichtlicher.

Aber was genau hält dich davon ab einfach den Array zu Casten, also ohne setlength? Ist es wirklich notwenig den Array zu Kopieren? Immerhin kostet das super viel Zeit im Gegensatz zum Pointer Cast.

Außerdem statt constructor TFunc.Create(param: array of byte; size: word); verwende am besten 1. TBytes statt array of byte, denn array of byte in Funktionsparametern ist ein offenes Array, kein dynamisches (was zu unerwarteten Problemen führen kann). Wenn man mit Arrays arbeitet, mehr als nur einmal eben in einer Funktion, lohnt es sich fast immer dafür einen eigenen Typen zu definieren, so umgehst du das open Array Problem. Für Bytes (TBytes), strings (TStringArray und viele mehr gibt es das auch schon vordefiniert 2. warum word als size? bist du wirklich sicher das die Anzahl an bytes kleiner 65000 ist? und selbst wenn ist deine CPU auf IntPtr deutlich schneller als auf 16 bit words (außer du hast einen Rechner aus den 80ern mit ner 16 bit CPU, dann ist UIntPTr = Word )

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 Warf,

ich habe mich nochmal an Deinen Vorschlag herangewagt, und es auch zum (bisher) fehlerfreien Laufen gebracht.
In dieser Form ist es (für mich) maximal elegant, weil überhaupt kein Kopieren notwendig ist.

Zu Deiner Frage: Den word Datentyp gibt das MDF Format vor (UINT16). Größer kann das Array nicht werden.

Bei dieser Gelegenheit gleich noch weitere Fragen (wenn ich darf?):
1. Was ist konkret der Unterschied zwischen einem offenen und einem dynamischen Array?
Ich kannte bisher nur statische (feste Größe) und dynamische Arrays
(zur Laufzeit veränderbare Größe mit SetLength(array, size), wie bekannt).

2. Das
SetLength(TableValueArray, size);
musste ich NACH dem
TableValueArray := TTableValueArray(param);
aufrufen, damit bei einem späteren Aufruf von
function TFunc.ASAM_MCD2_Text_TableFunc(x: double): string;
die Aufrufe von low(TableValueArray) und high(TableValueArray) den korrekten
Indizes vom TableValueArray entsprechen und nicht etwa den Indizes von
param (TBytes).
Das ist dann natürlich etwas tricky und hoffe das ist nicht als
"Schweinkram" in der Pascal Programierwelt verpöhnt:-)

Auf jeden Fall danke ich Euch allen für diese Lehrstunde in der Pascal Programmierung
und sicher werde ich den Ansatz mit dem TFileStream nutzen sobald die Anforderung entsprechend ist.

Danke nochmal und Gruß

Arne

Code: Alles auswählen

 
Type
  ASAM_MCD2_Text_TableFuncParam = packed record // type 11
    Value1:    double;
    ByteArray: array [0..31] of byte;
  end;
 
  TTableValueArray = array of ASAM_MCD2_Text_TableFuncParam;
 
  TFunc = class
  public
    ...
    TableValueArray: TTableValueArray;
 
    function ASAM_MCD2_Text_TableFunc(x: double): string;
 
    constructor Create(param: TBytes; size: word);
  end;
 
  constructor TFunc.Create(param: TBytes; size: word);
  begin
    TableValueArray := TTableValueArray(param);
    SetLength(TableValueArray, size);
  end;
 
  function TFunc.ASAM_MCD2_Text_TableFunc(x: double): string;
  var
    i: word;
  begin
    for i := low(TableValueArray) to high(TableValueArray) do
    begin
      if TableValueArray[i].Value1 = x then //ich weiss Double auf Gleicheit abzufragen
                                            //kann manchmal unerwartete Ergebnisse erzeugen.
                                            //Mit echten Daten/Dateien hat es aber (bisher) geklappt:-)
      begin
        result := FloatToStr(x) + #9 + ByteArrayToString(TableValueArray[i].ByteArray);
        break;
      end;
      result := 'Should never reach this!';
    end;
  end;

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 »

Helios hat geschrieben:Hallo Warf,

ich habe mich nochmal an Deinen Vorschlag herangewagt, und es auch zum (bisher) fehlerfreien Laufen gebracht.
In dieser Form ist es (für mich) maximal elegant, weil überhaupt kein Kopieren notwendig ist.

Zu Deiner Frage: Den word Datentyp gibt das MDF Format vor (UINT16). Größer kann das Array nicht werden.

Bei dieser Gelegenheit gleich noch weitere Fragen (wenn ich darf?):
1. Was ist konkret der Unterschied zwischen einem offenen und einem dynamischen Array?
Ich kannte bisher nur statische (feste Größe) und dynamische Arrays
(zur Laufzeit veränderbare Größe mit SetLength(array, size), wie bekannt).

2. Das
SetLength(TableValueArray, size);
musste ich NACH dem
TableValueArray := TTableValueArray(param);
aufrufen, damit bei einem späteren Aufruf von
function TFunc.ASAM_MCD2_Text_TableFunc(x: double): string;
die Aufrufe von low(TableValueArray) und high(TableValueArray) den korrekten
Indizes vom TableValueArray entsprechen und nicht etwa den Indizes von
param (TBytes).
Das ist dann natürlich etwas tricky und hoffe das ist nicht als
"Schweinkram" in der Pascal Programierwelt verpöhnt:-)

Zu 1.Siehe Hier und hier. somd einfach ein spezieller Array Typ der seine Eigenheiten hat. Wenn man einen Dynamischen array übergibt sollte man den auch als Typen definieren und den wählen.
2. Ich seh auch grade das sich der Cast nicht um die Längeninformationen kümmert, und dabei ein memory leak erzeugt. Funktioniert also doch nicht, dann würde ich Move an deiner stelle verwenden

Antworten