unpack von binärdaten
Niehztog
- php
Hallo,
1. Problem:
ich habe eine Datei, in die drei Zahlen wie folgt reingeschrieben werden:
$handle = fopen('test.bin', "a"));
fwrite($handle, pack ("VVV", 1262698832, 44927, 128)));
fclose($handle);
Mein Ziel ist es nun, diese Werte später auch wieder aus der Datei zu lesen. Momentan klappt es bei mir so:
$handle = fopen('test.bin', 'rb');
$data = fread($handle, 4);
$raw = unpack("V", $data);
$zahl1 = $raw[1];
$data = fread($handle, 4);
$raw = unpack("V", $data);
$zahl2 = $raw[1];
$data = fread($handle, 4);
$raw = unpack("V", $data);
$zahl3 = $raw[1];
fclose($handle);
Gibt es eine Möglichkeit, die Zahlen "eleganter" auszulesen - also mit einem einzigen unpack Befehl? Ich habe versucht unpack("VVV", $data), wobei in $data dann auch alles drin stand. Das klappte irgendwie nicht, es kam nur die erste Zahl heraus.
2. Problem:
Mit folgendem Befehl werden binärdaten gepackt:
$data = pack('a56@56VV', 'irgendeinstring', 44927, 128);
Hier versteh ich den pack Befehl noch nicht mal. Ich habe keine Ahnung wie ich die drei Ursprungswerte mit unpack zurück bekomme. Kann mir da jemand helfen?
mfG
Niehztog
hi,
Hier versteh ich den pack Befehl noch nicht mal. Ich habe keine Ahnung wie ich die drei Ursprungswerte mit unpack zurück bekomme. Kann mir da jemand helfen?
Du brauchst bei pack/unpack _stets_ die gleiche Schablone. Also, wenn Du 3 Zahlen packst, ist "VVV" ok, das ergibt eine bytefolge in "VAX" (little-endian) order.
Daraus kriegst Du mit
unpack "VVV", $binary;
eine Liste mit den drei Werten. Also Listen-Kontext beachten.
Hotti
Hallo Tom und hotti,
danke für eure Tips. Genau wie ihr das beschreibt hatte ich mir das auch vorgestellt, aber es klappt nicht. Folgender Code:
$var1 = pack('VVV', 1262698832, 44927, 128 );
$var2 = unpack('VVV', $var1);
var_dump($var2);
Output:
array(1) {
["VV"]=>
int(1262698832)
}
Wie man sieht gibt es nur ein Array-Element anstelle der erwarteten drei. Was läuft schief?
[latex]Mae govannen![/latex]
Hallo Tom und hotti,
danke für eure Tips. Genau wie ihr das beschreibt hatte ich mir das auch vorgestellt, aber es klappt nicht. Folgender Code:
$var1 = pack('VVV', 1262698832, 44927, 128 );
$var2 = unpack('VVV', $var1);
var_dump($var2);
'VVV'? Nö.
Du suchst das Wiederholungszeichen \* im Format-String
Cü,
Kai
--
~~~ ken SENT ME ~~~
Dank Hixies Idiotenbande geschieht grade eben wieder ein Umdenken
in Richtung "Mess up the Web".([suit](https://forum.selfhtml.org/?t=197497&m=1324775))
[SelfHTML-Forum-Stylesheet](http://selfhtml.knrs.de/#h_stylesheet)
Hello,
Hallo Tom und hotti,
danke für eure Tips. Genau wie ihr das beschreibt hatte ich mir das auch vorgestellt, aber es klappt nicht. Folgender Code:
$var1 = pack('VVV', 1262698832, 44927, 128 );
$var2 = unpack('VVV', $var1);
var_dump($var2);
>
> Output:
> ~~~
> array(1) {
> ["VV"]=>
> int(1262698832)
> }
>
Wie man sieht gibt es nur ein Array-Element anstelle der erwarteten drei. Was läuft schief?
Es ist auch sehr gewöhnungsbedürftig:
<?php ### pack_unpack.php ###
$var1 = pack('VVV', 1262698832, 44927, 128 );
$var2 = unpack('V1myvar/V1yourvar/V1ourvar', $var1);
var_dump($var2);
?>
das ergibt:
array(3) { ["myvar"]=> int(1262698832) ["yourvar"]=> int(44927) ["ourvar"]=> int(128) }
Zuerst kommt immer das Format/der Typ, dann der Wiederholungsfaktor und danach der Name des Array-Elementes.
Liebe Grüße aus dem schönen Oberharz
Tom vom Berg
Klasse, mit 'V1myvar/V1yourvar/V1ourvar' klappt es, ebenso wie mit 'V*' und 'V3'. Vielen Dank dafür.
Eine letzte Frage hätte ich noch (ich lese tatsächlich ein externes Dateiformat ein): Gibt es auch einen Format String, der bewirkt, dass die binärdaten nur bis zum ersten NUL Byte gelesen werden? Im konkreten Fall kommt erst ein 56 byte langer string und dann zwei 4 byte integer. Allerdings kommt es vor, dass im String (der nicht immer die ganzen 56 byte ausnutzt) nach dem ersten NUL Byte noch mülldaten kommen, die von unpack dann als komische Zeichen an den entpackten String drangehängt werden.
Klar, man könnte den Sting byte für byte lesen und nach dem ersten NUL Byte aufhören, aber vielleicht geht das ja auch "eleganer"?
Hello,
Klasse, mit 'V1myvar/V1yourvar/V1ourvar' klappt es, ebenso wie mit 'V*' und 'V3'. Vielen Dank dafür.
Eine letzte Frage hätte ich noch (ich lese tatsächlich ein externes Dateiformat ein): Gibt es auch einen Format String, der bewirkt, dass die binärdaten nur bis zum ersten NUL Byte gelesen werden? Im konkreten Fall kommt erst ein 56 byte langer string und dann zwei 4 byte integer. Allerdings kommt es vor, dass im String (der nicht immer die ganzen 56 byte ausnutzt) nach dem ersten NUL Byte noch mülldaten kommen, die von unpack dann als komische Zeichen an den entpackten String drangehängt werden.
Klar, man könnte den Sting byte für byte lesen und nach dem ersten NUL Byte aufhören, aber vielleicht geht das ja auch "eleganer"?
Lies einen Block mit einer byteorientierten Dateifunktion ein, werte ihn mit einer ebensolchen "Stringfunktion" aus.
$packed_values = '';
if(($pos = strpos($buffer, chr(0))!==0)
{
$packed_values = substr($buffer, 0, $pos);
}
und entscheide dann, wieviele Werte Du auswerten musst.
http://de.php.net/manual/en/function.strpos.php
http://de.php.net/manual/en/function.substr.php
Soweit ich mich erinnere, kann man bei der Angabe der Maske für die Decodierung (unpack) der Werte auch mehr angeben, als nachher tatsächlich vorhanden sind. Das solltest Du aber selber nochmal ausprobieren. Das hieße, dass Du nur den auszuwertenden Bytestrom entsprechend obigem Beispiel kürzen müsstest und dann nur soviele Werte daraus generiert werden, wie Futter dafür vorhanden ist.
Liebe Grüße aus dem schönen Oberharz
Tom vom Berg
Hello,
$packed_values = '';
###> if(($pos = strpos($buffer, chr(0))!==0)
if(($pos = strpos($buffer, chr(0))!==false)
{
$packed_values = substr($buffer, 0, $pos);
}und entscheide dann, wieviele Werte Du auswerten musst.
http://de.php.net/manual/en/function.strpos.php
http://de.php.net/manual/en/function.substr.phpSoweit ich mich erinnere, kann man bei der Angabe der Maske für die Decodierung (unpack) der Werte auch mehr angeben, als nachher tatsächlich vorhanden sind. Das solltest Du aber selber nochmal ausprobieren. Das hieße, dass Du nur den auszuwertenden Bytestrom entsprechend obigem Beispiel kürzen müsstest und dann nur soviele Werte daraus generiert werden, wie Futter dafür vorhanden ist.
Liebe Grüße aus dem schönen Oberharz
Tom vom Berg
Hallo,
$packed_values = '';
if(($pos = strpos($buffer, chr(0))!==false)
{
$packed_values = substr($buffer, 0, $pos);
}und entscheide dann, wieviele Werte Du auswerten musst.
Hat so wunderbar geklappt. Gute Idee es so zu lösen (auch wenn strpos intern wahrscheinlich auch über die einzelnen Zeichen iteriert - sieht so aber schöner aus). ;-)
$pos = strpos($buffer, chr(0));
$data[] = unpack('a' . ( $pos ? (string)$pos : '56' ) . 'var1/@56/V1var2/V1var3', $buffer);
Vielen Dank!
hi,
Klar, man könnte den Sting byte für byte lesen und nach dem ersten NUL Byte aufhören, aber vielleicht geht das ja auch "eleganer"?
Die Schablone "Z*" liefert mit pack einen Nullterminierten String. Genauso kriegst Du den mit unpack("Z*", $bin); wieder raus. Alles, was danach kommt, wird dann ignoriert, zumindest habe ich das mal eben mit Perl getestet. Du könntest evntl. danach die Position des Dateizeigers (im Handle) abfragen, damit die Datei weiter gelesen werden kann (experimental, nicht getestet).
Hotti
Hi hotti
Die Schablone "Z*" liefert mit pack einen Nullterminierten String.
Gerade getestet: Type Z: unknown format code
Sieht so aus, dass Z in PHP noch nicht implementiert ist. Wenn doch, hätte es mir sicher geholfen und wäre vermutlich an dieser Stelle genau das richtige. Schonmal gut zu wissen, dass es dann wohl also wirklich nicht anders geht als Byte für Byte einzulesen.
Niehztog
hi,
Die Schablone "Z*" liefert mit pack einen Nullterminierten String.
Gerade getestet: Type Z: unknown format code
Sieht so aus, dass Z in PHP noch nicht implementiert ist. Wenn doch, hätte es mir sicher geholfen und wäre vermutlich an dieser Stelle genau das richtige. Schonmal gut zu wissen, dass es dann wohl also wirklich nicht anders geht als Byte für Byte einzulesen.
Oder: Machs mit Perl ;-)
Mein Hobby seit über 10 Jahren, und so richtig "Gas gegeben mit Perl" habe ich im ersten Quartal dieses Jahr, indem ich ein paar eigene Module geschrieben habe zum Speichern von Objekten in Binär(raw)dateien.
Auf jeden Fall finde ich es begrüßenswert, dass sich da auch in Sachen PHP was tut, an der Web-Front, wo es auf Performance ankommt, sind Binärdateien in vielen Fällen einer DB-Anbindung klar überlegen ohne den Anspruch zu erheben, dass eine solche Datenhaltung bspw. eine MySQL-Anbindung grundsätzlich ersetzen kann. Das ist freilich von Fall zu Fall abzuwägen.
Viel Spaß weiterhin,
Horst
Hello,
Mein Ziel ist es nun, diese Werte später auch wieder aus der Datei zu lesen. Momentan klappt es bei mir so:
Gibt es eine Möglichkeit, die Zahlen "eleganter" auszulesen - also mit einem einzigen unpack Befehl?
Unpack() liefert ein Array mit den Elementen. Die Namen der Arrayelemente gibst Du durch die passende Schablone vor.
http://de.php.net/manual/en/function.unpack.php
http://de.php.net/manual/en/function.pack.php
Liebe Grüße aus dem schönen Oberharz
Tom vom Berg
Hello,
wenn es nicht um vorgegebene Dateiformate geht und die Anzahl der Datenwerte überschaubar belibt, dann sind innerhalb von PHP-Applikationen (also ohne Zugriff auf Fremddaten) serialize() und unserialize() eher zu empfehlen
http://de.php.net/manual/en/function.serialize.php
http://de.php.net/manual/en/function.unserialize.php
Liebe Grüße aus dem schönen Oberharz
Tom vom Berg