Hi!
Ich verwende jetzt keine Dummydaten mehr, sondern habe eine Binärdatei[1] geöffnet[2]. Ich habe parallel eine ASCII Datei[1] erhalten, die den Inhalt der Binärdatei enthält, um zu testen, ob ich die Binärdatei richtig entpacke.
Der Aufbau ist so: erst kommt ein Integerwert und dann folgen in Anzahl dieses Integers Floats.
Mit
unpack("L*", $data_bin)
sehe ich1 => int 4
2 => int 600
3 => int 4
4 => int 8wobei ich die 600 lt. Ascii-Datei erwartet habe. Die "4"er sind Phantasie.
Die 8 gehört bereits zum ersten Wert. Wenn du dir mal die Datei in einem Hex-Editor anschaust, siehst du x04, x00, x00, x00, gefolgt von x58, x02, x00, x00 und nochmal x04, x00, x00, x00. Das x58 x02 ergibt richtigrum gedreht 258 hex = 600 dez. Mein Editor zeigt 16 Byte pro Zeile und eine Zeilennummer, nein, eigentlich eine Adresse an. Die Adresse der letzten Zeile ist 00002580, passt also zu dem 258/600. Zu sehen ist außerdem, dass in der Zeile 4 Byte fehlen, also besteht ein Eintrag aus den letzten 4 Byte einer Zeile plus 12 Byte der nachfolgenden Zeile. Auf die erste Zeile angewendet, gehört also deine 8 bereits zum ersten Wert.
Und nun versagen meine Erklärungsversuche teilweise. Ein Float hat entweder 32 oder 64 Bit, also 4 oder 8 Byte. Ein Wert in deiner Datei sind aber 16 Byte und wenn man da hinschaut, sind die 8 Bytes eines Float vorn und hinten mit 08 00 00 00 eingerahmt. Und der eigentliche Sinn davon erschließt sich mir nicht.
Wenn ich mir die gleiche Datei mit
unpack("d*", $data_bin)
ansehe, erhalte ich:1 => float 1.2731974745811E-311
2 => float 1.6975966329698E-313
3 => float 0.68123407969334
4 => float 1.6975966331675E-313
5 => float 0.58200506209549
Der erste Eintrag ist die Floatinterpretation von 04 00 00 00 58 02 00 00 und ist nicht sinnvoll, weil das ja zwei Integer sind und kein Float. Der zweite Eintrag ist der Float von 04 00 00 00 08 00 00 00 und ebenfalls nicht sinnvoll. Die 4 ist der Nachspann der Längenangabe, die 8 der Vorspann vom ersten Float. Und dieser folgt an Position 3. Verglichen mit der Auflösung (6.81234e-001) stimmt der. Dann kommt eine 8 als Nachspann und noch eine als nächster Vorspann, die sich im Element 4 wiederfinden, gefolgt von der nächsten sinnvollen Zahl in Element 5 (Auflösung: 5.82005e-001).
Die Phantasiewerte erkläre ich mir momentan damit, dass evtl 64Bit == 8Byte für die Floats und Integers reserviert wurden, L aber nur 4Byte abfragt. (
?#2
: Richtig?)
Nein, schau in den Hexeditor und probier auch mal unpack('d', "\x89\x83\xa6\x69\xab\xcc\xe5\x3f"), was die 8 Byte an Adresse 10 sind, also dein erster sinnvoller Float-Wert. Die Phantasiewerte scheinen mir eine Längenangabe zu sein, die aber immer doppelt auftritt, einmal als Vorspann und nochmal als Nachspann. Und das auch bei der Angabe der Anzahl der Floatwerte. Diese Vor- und Nachspänne bringen dir keine Punkte, denn die Längen des Zählwertes und der Nutzwerte sind fest. Es sei denn, das ändert sich von Datei zu Datei, aber auch dann wäre nur ein Vorspann nötig. Jedenfalls kannst und musst du diese "Phantasiewerte" überspringen.
Nun würde ich gerne eine Maske schreiben, die mir die Daten perfekt entpackt.
Ganz gut passt:
$out = unpack('L2ints/d*floats', $data_bin);
Ergebniss:
array
'ints1' => int 4
'ints2' => int 600
'floats1' => float 1.6975966329698E-313
Das floats1 ist der Wert aus dem Nachspann der 600 und dem Vorspann des ersten Wertes.
'floats2' => float 0.68123407969334
'floats3' => float 1.6975966331675E-313
'floats4' => float 0.58200506209549oder
$out = unpack("L4ints/d*floats", $data_bin);
array
'ints1' => int 4
'ints2' => int 600
'ints3' => int 4
'ints4' => int 8
Damit teilst du den 600er Nachspann und den ersten Vorspann in zwei Integer-Werte auf.
'floats1' => float 0.68123407969334
Die Unterschiede deiner beiden Versuche ist somit, dass die Nutzwerte entweder in geraden oder ungeraden Feldnummern stehen. Sowohl das eine als auch das andere kann nützlicher sein, je nachdem, wie du im weiteren Verlauf auf jeden zweiten Eintrag zuzugreifen gedenkst.
Ähnliches mit L3 oder L5 zerhaut mir die Daten und ich erhalte keinen Wert mehr von meinen erwarteten Floats.
Damit zerstückelst du dir deine Floats, weil der Lesezeiger immer genau auf ihrer Mitte landet.
?#4
: Warum wandert der erste korrekte Floatwert von Position 5 bei L2 auf Position 4 bei L4? Ich hätte erwartet, dass er bei Position 5 bleibt und nur mehr Phantasiewerte dazwischen stehen...
Dürfte bereits geklärt sein.
?#5
: Wie kann ich durch einen geeigneten Formatanweiser 64Bit lesen, da ich annehme der Autor hat die Floats in 64 kodiert. Gibt es da was in php 5.3.5? Oder kann ich die Lücken bedenkenlos wegschmeißen, auch in Hinblick auf ein späteres evtl. neg. Vorzeichen?
Es sind keine 64-Bit-Floats. Es bleiben 32 Bit mit 32 Bit Müll dazwischen.
[2]
?#1
: Was ist besser?
$file_hdl = fopen($filename,"rb");
$data_bin = fread($file_hdl, filesize($filename));
fclose($file_hdl);
>
> oder
>
> `$data_bin = file_get_contents($filename);`{:.language-php}
Da du unpack() den gesamten Batzen übergibst, ist letzeres effizienter zu notieren. fread() ist nur bei häppchenweisem Lesen von Vorteil.
Lo!