(ERLEDIGT)Zahlen aus Speicher/Binärdatei lesen.

Für Fragen zur Programmiersprache auf welcher Lazarus aufbaut
Antworten
Soner
Beiträge: 623
Registriert: Do 27. Sep 2012, 00:07
OS, Lazarus, FPC: Win10Pro-64Bit, Immer letzte Lazarus Release mit SVN-Fixes
CPU-Target: x86_64-win64
Wohnort: Hamburg

(ERLEDIGT)Zahlen aus Speicher/Binärdatei lesen.

Beitrag von Soner »

Ich muß eine Zahl aus dem Speicher mittels Reader lesen. Damit ich nicht byte für byte lesen muß verwende ich folgenden code.
Ist denn mein Vorgehen richtig oder wird es bei Bit-Endigan-Maschinen Fehler verursachen?

Code: Alles auswählen

 
var i: LongWord; //longword =4byte
begin
  //00000012 <-- Im Speicher/Datei steht die Zahl so, 4 byte hex-zahl
 
  //Hier wird gelesen
  TBinaryObjectReader1.Read(i,4);
 
  //danach ist i=301989888 als $12000000
 
  // ich schiebe i 3 Bytes rechts dann wird draus $00000012
  i:=i shr 24;
 
  // ist also richtig
  // muß man für die Big-Endian-Maschinen 3 Bytes links schieben?
end;
 
Zuletzt geändert von Soner am Mo 13. Mär 2017, 23:33, insgesamt 1-mal geändert.

siro
Beiträge: 730
Registriert: Di 23. Aug 2016, 14:25
OS, Lazarus, FPC: Windows 11
CPU-Target: 64Bit
Wohnort: Berlin

Re: Zahlen aus Speicher/Binärdatei lesen.

Beitrag von siro »

Einen schönen guten Morgen.

Nein, das ist nicht richtig mit dem Schieben. Das funktioniert hier nur weil Dein Wert kleiner also 255 ist, also in ein Byte passt.
Bei negativen Werten geht das völlig schief.

Du musst beim Konvertieren von Litte Endian zu Big Endian und umgekehrt ALLE Bytes vertauschen.
Byte 1 wird Byte 4
Byte 2 wird Byte 3
Byte 3 wird Byte 2
Byte 4 wird Byte 1

Wenn Du zum Beispiel die Zahl 123456789 Im Speicher hast dann sieht das so aus:
Hexadezimal ist das 075BCD15
Dateianhänge
litte_Big_Endian.jpg
Grüße von Siro
Bevor ich "C" ertragen muß, nehm ich lieber Lazarus...

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

Re: Zahlen aus Speicher/Binärdatei lesen.

Beitrag von wp_xyz »

Routinen zur Little-Big-Endian-Konvertierung sind eingebaut:
- SwapEndian
- BEToN
- LEToN
- NToBE
- NToLE
wobei N die Endianness deines Systems ist. Die Routinen sind überladen für alle möglichen Datentypen. Für Widestrings habe ich folgendes im fpspreadsheet:

Code: Alles auswählen

function WideStringToLE(const AValue: WideString): WideString;
{$IFNDEF FPC}
var
  j: integer;
{$ENDIF}
begin
  {$IFDEF FPC}
    {$IFDEF FPC_LITTLE_ENDIAN}
      Result:=AValue;
    {$ELSE}
      Result:=AValue;
      for j := 1 to Length(AValue) do begin
        PWORD(@Result[j])^:=NToLE(PWORD(@Result[j])^);
      end;
    {$ENDIF}
  {$ELSE}
    Result:=AValue;
  {$ENDIF}
end;
 

Soner
Beiträge: 623
Registriert: Do 27. Sep 2012, 00:07
OS, Lazarus, FPC: Win10Pro-64Bit, Immer letzte Lazarus Release mit SVN-Fixes
CPU-Target: x86_64-win64
Wohnort: Hamburg

Re: Zahlen aus Speicher/Binärdatei lesen.

Beitrag von Soner »

Danke euch beiden, das mit dem Bit-Shiften ist Quatsch, war gestern wahrscheinlich am Ende zu müde.

wp_xyz hat geschrieben:...
Für Widestrings habe ich folgendes im fpspreadsheet:

Bist du der wp aus dem englischen Forum, der an Fpspreadsheet arbeitet?
Wenn das so ist dann haben wir uns ja schon dort begegnet und ich gratuliere dir du machst sehr gute Arbeit mit fpspreadsheet.
Das Bespiel Spready sieht auch ziemlich gut aus. Ich habe zum Anschauen einige meine ExcelXp-Tabellen geladen, es sieht wie bei Excel aus.

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

Re: Zahlen aus Speicher/Binärdatei lesen.

Beitrag von Mathias »

Routinen zur Little-Big-Endian-Konvertierung sind eingebaut:
- SwapEndian
- BEToN
- LEToN
- NToBE
- NToLE

Die kannte ich gar nicht, ich selbst was gebastelt, das ich kompatibel zu Java war.

Gibt es so was auch für Single und Double ?

Dieser Versuch mit Typenumwandlung scheitert.

Code: Alles auswählen

var
  f: single;
begin
  f := 123.456;
 
  f := single(SwapEndian(Int32(f)));
  f := single(SwapEndian(Int32(f)));
 
  ShowMessage(FloatToStr(f));
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: Zahlen aus Speicher/Binärdatei lesen.

Beitrag von wp_xyz »

@soner: Ja, der bin ich.

@Mathias: Anscheinend nicht.

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

Re: Zahlen aus Speicher/Binärdatei lesen.

Beitrag von Mathias »

Ich habe versucht die, SwapEndian-Funktion von DWord abzuändern, aber funktioniert nicht, der Single bleibt unverändert.
Legt FPC ein Single anders im Speicher ab, als ein DWord / LongInt ?

Code: Alles auswählen

function Swap2(const AValue: Single): Single; assembler; nostackframe;
asm
{$ifdef win64}
  movl %ecx, %eax
{$else win64}
  movl %edi, %eax
{$endif win64}
  bswap %eax
end;
 
procedure TForm1.Button1Click(Sender: TObject);
var
  f:Single;
begin
  f:=123.456;
//  BEtoN();
 
  f := Swap2(f);
  ShowMessage(FloatToStr(f));
  f := Swap2(f);
  ShowMessage(FloatToStr(f));
end;
Zuletzt geändert von Mathias am Mo 13. Mär 2017, 18:58, insgesamt 1-mal geändert.
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: Zahlen aus Speicher/Binärdatei lesen.

Beitrag von wp_xyz »

Ich weiß nicht, was der Compiler mit diesen Word-Integer-Casts macht...

Was soll in deinem Code das verwaiste BEToN()? Ohne Argument akzeptiert das der Compiler doch gar nicht?! Und es ist eine Funktion, deren Ergebnis du unter den Tisch fallen lässt.

So geht's bei mir:

Code: Alles auswählen

program Project1;
 
{$mode objfpc}{$H+}
 
uses
  SysUtils;
 
function SwapEndian(s: Single): Single;
const
  n = SizeOf(Single) - 1;
var
  b: packed array[0..n] of byte absolute s;
  br: packed array[0..n] of byte absolute Result;
  i: Integer;
begin
  for i:=0 to n do
    br[i] := b[n-i];
end;
 
function SwapEndian(d: Double): Double;
const
  n = SizeOf(Double) - 1;
var
  b: packed array[0..n] of byte absolute d;
  br: packed array[0..n] of byte absolute Result;
  i: Integer;
begin
  for i:=0 to n do
    br[i] := b[n-i];
end;
 
function SwapEndian(e: Extended): Extended;
const
  n = SizeOf(Extended) - 1;
var
  b: packed array[0..n] of byte absolute e;
  br: packed array[0..n] of byte absolute Result;
  i: Integer;
begin
  for i:=0 to n do
    br[i] := b[n-i];
end;
 
 
var
  s, s1, s2: Single;
  d, d1, d2: Double;
  e, e1, e2: Extended;
begin
  WriteLn('Single:');
  s := 123.456;
  WriteLn(FloatToStr(s));
 
  s1 := SwapEndian(s);
  Writeln(FloatToStr(s1));
 
  s2 := SwapEndian(s1);
  WriteLn(FloatToStr(s2));
 
  // ---
 
  WriteLn;
  WriteLn('Double:');
  d := 123.456;
  WriteLn(FloatToStr(d));
 
  d1 := SwapEndian(d);
  Writeln(FloatToStr(d1));
 
  d2 := SwapEndian(d1);
  WriteLn(FloatToStr(d2));
 
  // ----
 
  WriteLn;
  WriteLn('Extended:');
  e := 123.456;
  WriteLn(FloatToStr(e));
 
  e1 := SwapEndian(e);
  WriteLn(FloatToStr(e1));
 
  e2 := SwapEndian(e1);
  WriteLn(FloatToStr(e2));
 
  ReadLn;
 
end.
 

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

Re: Zahlen aus Speicher/Binärdatei lesen.

Beitrag von Mathias »

Was soll in deinem Code das verwaiste BEToN()?

Dies muss natürlich weg, habe es versehentlich mit kopiert.

Ich bin auf die fast gleiche Lösung gekommen, welche funktioniert.
Das mit dem SizeOf von dir sicherer.

Code: Alles auswählen

function Swap(const f: single): single;
var
  q: array[0..3] of byte absolute f;
  z: array[0..3] of byte absolute Result;
  i: integer;
begin
  for i := 0 to 3 do begin
    z[i] := q[3 - i];
  end;
end


Ich weiß nicht, was der Compiler mit diesen Word-Integer-Casts macht...

Auf jeden Fall was anderes, als ich erwartete.

Wäre das eine Idee, wen man das SwapEndian für Real-Zahlen, dem FPC-Team melden würde.
Ich denke, es gibt noch mehrere User, die solche Zahlen mit Java austauschen wollen.
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: Zahlen aus Speicher/Binärdatei lesen.

Beitrag von wp_xyz »

Mathias hat geschrieben:Wäre das eine Idee, wen man das SwapEndian für Real-Zahlen, dem FPC-Team melden würde.
Ich denke, es gibt noch mehrere User, die solche Zahlen mit Java austauschen wollen.

Also dann: Auf zum Englisch üben und einen Report für den Bugtracker schreiben!

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

Re: Zahlen aus Speicher/Binärdatei lesen.

Beitrag von Mathias »

Also dann: Auf zum Englisch üben und einen Report für den Bugtracker schreiben!


Ich frage direkt bei: fpc-devel@lists.freepascal.org
Mit Lazarus sehe ich grün
Mit Java und C/C++ sehe ich rot

marcov
Beiträge: 1100
Registriert: Di 5. Aug 2008, 09:37
OS, Lazarus, FPC: Windows ,Linux,FreeBSD,Dos (L trunk FPC trunk)
CPU-Target: 32/64,PPC(+64), ARM
Wohnort: Eindhoven (Niederlande)

Re: Zahlen aus Speicher/Binärdatei lesen.

Beitrag von marcov »

Am einfachsten kopieren man den TStreamHelper aus unit packages/fcl-base/src/streamex.pp, aber man soll es von TReader nach TAbstractReader umschreiben. In ein Unit rein werfen und diese USES'n.

Dann kann man einfach

Code: Alles auswählen

  x:=TBinaryObjectReader1.ReadDwordBE  


schreiben, und das wird gut gehen auf bigendian und little Endian Platformen. Das Package CHM macht etwas aehnliches.


Ein andere Möglichkeit der etwas mehr Spaß macht ist:

Code: Alles auswählen

result:=swap(word(x)) shl 16 +  swap(word (x shr 16));

Soner
Beiträge: 623
Registriert: Do 27. Sep 2012, 00:07
OS, Lazarus, FPC: Win10Pro-64Bit, Immer letzte Lazarus Release mit SVN-Fixes
CPU-Target: x86_64-win64
Wohnort: Hamburg

Re: (ERLEDIGT)Zahlen aus Speicher/Binärdatei lesen.

Beitrag von Soner »

Wie ich später festgestellt habe, es war kein Endian-Geschichte sondern Streaming-Fehler wegen SizeOf(EnumType), mein Fehler habe nicht geachtet.

Antworten