Delicious Bookmark this on Delicious Share on Facebook SlashdotSlashdot It! Digg! Digg



PHP : Function Reference : Miscellaneous Functions : unpack

unpack

Unpack data from binary string (PHP 4, PHP 5)
array unpack ( string format, string data )

Unpacks from a binary string into an array according to the given format.

unpack() works slightly different from Perl as the unpacked data is stored in an associative array. To accomplish this you have to name the different format codes and separate them by a slash /.

Parameters

format

See pack() for an explanation of the format codes.

data

The packed data.

Return Values

Returns an associative array containing unpacked elements of binary string.

Examples

Example 1367. unpack() example

<?php
$array
= unpack("c2chars/nint", $binarydata);
?>

The resulting array will contain the entries "chars1", "chars2" and "int".


Notes

Caution:

Note that PHP internally stores integral values as signed. If you unpack a large unsigned long and it is of the same size as PHP internally stored values the result will be a negative number even though unsigned unpacking was specified.

See Also
pack()

Code Examples / Notes » unpack

magicaltux

tomlove at gmail dot com gave a code to easily unpack a string. Here's a variant :
<?php
$packed = pack("s*", 123);
list($unpacked) = unpack("s*0", $packed);
?>
$unpacked now holds the value 123, not an array.
unpack() will use numeric keys if you give only numbers (probably same check as is_numeric(), but I didn't test). This can be useful in some cases (like for getting directly your data without using arrays).


david alsbury

This is the best example I have found to use unpack.  I was finally able to make sense of the documentation afters seeing how they used the function.
http://fooassociates.com/phpfer/html/rn45re878.html
Quick Example:
<?
header("Content-type: text/plain");
/*
N unsigned long (always 32 bit, big endian byte order)
N = 4 bytes
"N2int_var" will read 8 bytes into 2 vars int_var1 and int_var2
binary string A324 = decimal 1,093,874,228
binary string &*12 = decimal 640,299,314
C unsigned char
"Cchar_var" will read 1 byte into char_var
binary string J = decimal 74
*/
$assoc_array = unpack("N2int_var/Cchar_var", "A324&*12J");
echo "int_var1 = ", $assoc_array['int_var1'], "\n";
echo "int_var2 = ", $assoc_array['int_var2'], "\n";
echo "char_var = ", $assoc_array['char_var'], "\n";
?>
Sample Output:
int_var1 = 1093874228
int_var2 = 640299314
char_var = 74


sergio santana: ssantana

This is about the last example of my previous post. For the sake of clarity, I'm including again here the example, which expands the one given in the formal documentation:
<?
 $binarydata = "AA\0A";
 $array = unpack("c2chars/nint", $binarydata);
 foreach ($array as $key => $value)
    echo "\$array[$key] = $value
\n";
?>
This outputs:
$array[chars1] = 65
$array[chars2] = 65
$array[int] = 65
Here, we assume that the ascii code for character 'A' is decimal 65.
Remebering that the format string structure is:
<format-code> [<count>] [<array-key>] [/ ...],
in this example, the format string instructs the function to
 1. ("c2...") Read two chars from the second argument ("AA ...),
 2. (...chars...) Use the array-keys "chars1", and "chars2" for
     these two chars read,
 3. (.../n...) Read a short int from the second argument (...\0A"),
 4. (...int") Use the word "int" as the array key for the just read
     short.
I hope this is clearer now,
Sergio.


carsten meier

The previous code has an error:
$low = $val & 65536;
has to be:
$low = $val & 65535;
Otherwise only bit #16 gets masked an not the lower 16 bit.


sergio santana: ssantana

Suppose we need to get some kind of internal representation of an integer, say 65, as a four-byte long. Then we use, something like:
<?
 $i = 65;
 $s = pack("l", $i); // long 32 bit, machine byte order
 echo strlen($s) . "
\n";
 echo "***$s***
\n";
?>
The output is:
X-Powered-By: PHP/4.1.2
Content-type: text/html
4
***A***
(That is the string "A\0\0\0")
Now we want to go back from string "A\0\0\0" to number 65. In this case we can use:
<?
 $s = "A\0\0\0"; // This string is the bytes representation of number 65
 $arr = unpack("l", $s);
 foreach ($arr as $key => $value)
    echo "\$arr[$key] = $value
\n";
?>
And this outpus:
X-Powered-By: PHP/4.1.2
Content-type: text/html
$arr[] = 65
Let's give the array key a name, say "mykey". In this case, we can use:
<?
 $s = "A\0\0\0"; // This string is the bytes representation of number  65
 $arr = unpack("lmykey", $s);
 foreach ($arr as $key => $value)
    echo "\$arr[$key] = $value\n";
?>
An this outpus:
X-Powered-By: PHP/4.1.2
Content-type: text/html
$arr[mykey] = 65
The "unpack" documentation is a little bit confusing. I think a more complete example could be:
<?
 $binarydata = "AA\0A";
 $array = unpack("c2chars/nint", $binarydata);
 foreach ($array as $key => $value)
   echo "\$array[$key] = $value
\n";
?>
whose output is:
X-Powered-By: PHP/4.1.2
Content-type: text/html
$array[chars1] = 65
$array[chars2] = 65
$array[int] = 65
Note that the format string is something like
<format-code> [<count>] [<array-key>] [/ ...]
I hope this clarifies something
Sergio


cristiano - finatofinato

STORE FILES IN DATABASE SQL_SERVER
After days of research, I obtained a solution to my "big-problem" working with php5/sqlserver/iis
//IN PHP.INI
//Valid range 0 - 2147483647.  Default = 4096.
mssql.textsize = 2147483647
=============<INSERT>==================
$dataString = file_get_contents($_FILES['myfile']['tmp_name']);
$arrData = unpack("H*hex", $dataString);
$ds = "0x".$arrData['hex'];
$sql = "SET TEXTSIZE 2147483647";
mssql_query($sql, $conn);
$sql = " insert into table (myimage, name, extension) values (".$ds.", '".$name."', '".$extension."') ";
mssql_query($sql, $conn);
=============</INSERT>==================
=============<RETRIEVE>==================
$sql = "SET TEXTSIZE 2147483647";
@mssql_query($sql, $conn);
$sql = " select myimage, name, extension from table ";
$rs = @mssql_query($sql, $conn);
header("Content-Disposition: attachment; filename=".$rs['name'].".".$rs['extension']." ");
echo $rs['myfile'];
=============</RETRIEVE>==================
with apache does not work in some cases (work with txt files, but not work with pdf, .doc....), and you will try to do another solution (the solution is more simple than this of sqlserver)


ein

If you need to unpack an unsigned long that's larger than php's limit (2^31-1) and don't mind doing a lot of fiddling, an easy solution is to unpack it as two shorts.
For example
<?php
$val = 2147483647; // we can't actually assign a number larger than 2^31-1 in php to an int, but if you're reading it from outside, it's easy to get larger than that.
// split it into two 16 bit words
$high= $val>>16;
$low = $val & 65536;
$packed= pack('nn',$high,$low);
// we have a 32bit long packed number assigned to $packed
$unpacked = unpack('nhigh\nlow',$packed);
$final = ($unpacked['high'] << 16) + $unpacked['low'];
?>
Of course this works equally as well as unpacking an externally created unsigned long.
This is most usefull when dealing with bitmasks, where the high and low word don't need to be connected.
Working with signed values is also possible, but I don't have any code examples to share (no need for them myself).


adam

If you just want to extract a dword/long int from a binary string, the following code works beautifully (intel endian):
$Number = ord($Buffer{0}) | (ord($Buffer{1})<<8) | (ord($Buffer{2})<<16) | (ord($Buffer{3})<<24);


tomlove

If you have one packed item, here's a quick way to get it out of its array when unpacking:
<?
$packed = pack("s*", 123);
list(,$unpacked) = unpack("s*", $packed);
?>
$unpacked now holds the value 123, not an array.


elpaso

If you are trying to make unpack 'N' work with unsigned long on 64 bit machines, you should take a look to this bug:
http://bugs.php.net/bug.php?id=40543
An ugly workaround for me was:
//Read a 4-byte integer from file
     $_r = fread($f,4);
     $a=unpack('Ni',$_r);
     $b = sprintf("%b", $a['i']); // binary representation
     if(strlen($b) == 64){
          $new = substr($b, 33);
          $a['i'] = bindec($new);
      }


danrichter.at.programmer.dot.net

If no key name is given [e.g., unpack('C*',$data)], the keys are simply integers starting at 1, and you have a standard array. (I know of no way to get the array to start at zero.)



If you use multiple types, you must give a key name for all of them (except optionally one), because the key counter is reset with each slash. For example, in unpack('n2/C*',$data), indices 1 and 2 of the returned array are filled by integers ('n'), then overwritten with characters ('C').


kennwhite dot nospam

If having a zero-based index is useful/necessary, then instead of:
$int_list = unpack("s*", $some_binary_data);
try:
$int_list = array_merge(unpack("s*", $some_binary_data));
This will return a 0-based array:
$int_list[0] = x
$int_list[1] = y
$int_list[2] = z
...
rather than the default 1-based array returned from unpack when no key is supplied:
$int_list[1] = x
$int_list[2] = y
$int_list[3] = z
...
It's not used often, but array_merge() with only one parameter will compress a sequentially-ordered numeric-index, starting with an index of [0].


justin dot spahrsummers

I hadn't realized that if the number after the unpack type was 1 (i.e. "V1page"), that it would behave as if there was no number at all. I had been using a variable and didn't think to watch for this. For instance,
<?php
if ($something)
  $get = 2;
else
  $get = 1;
$arr = unpack("V" . $get . "page", $data);
?>
Now if $something was FALSE, then $arr will only have one entry named "page". If $something was TRUE, $arr would have "page1" and "page2".


jjfoerch

I had a situation where I had to unpack a file filled with little-endian order double-floats in a way that would work on either little-endian or big-endian machines.  PHP doesn't have a formatting code that will change the byte order of doubles, so I wrote this workaround.
<?php
/*The following code is a workaround for php's unpack function
which does not have the capability of unpacking double precision
floats that were packed in the opposite byte order of the current
machine.
*/
function big_endian_unpack ($format, $data) {
   $ar = unpack ($format, $data);
   $vals = array_values ($ar);
   $f = explode ('/', $format);
   $i = 0;
   foreach ($f as $f_k => $f_v) {
$repeater = intval (substr ($f_v, 1));
if ($repeater == 0) $repeater = 1;
if ($f_v{1} == '*')
{
   $repeater = count ($ar) - $i;
}
if ($f_v{0} != 'd') { $i += $repeater; continue; }
$j = $i + $repeater;
for ($a = $i; $a < $j; ++$a)
{
   $p = pack ('d',$vals[$i]);
   $p = strrev ($p);
   list ($vals[$i]) = array_values (unpack ('d1d', $p));
   ++$i;
}
   }
   $a = 0;
   foreach ($ar as $ar_k => $ar_v) {
$ar[$ar_k] = $vals[$a];
++$a;
   }
   return $ar;
}
list ($endiantest) = array_values (unpack ('L1L', pack ('V',1)));
if ($endiantest != 1) define ('BIG_ENDIAN_MACHINE',1);
if (defined ('BIG_ENDIAN_MACHINE')) $unpack_workaround = 'big_endian_unpack';
else $unpack_workaround = 'unpack';
?>
This workaround is used like this:
<?php
function foo() {
       global $unpack_workaround;
$bar = $unpack_workaround('N7N/V2V/d8d',$my_data);
//...
}
?>
On a little endian machine, $unpack_workaround will simply point to the function unpack.  On a big endian machine, it will call the workaround function.
Note, this solution only works for doubles.  In my project I had no need to check for single precision floats.


info

Here is my solution to reading a Big-Endian formatted double on an Little-Endian machine.
<?php
function ToDouble($data) {
$t = unpack("C*", pack("S*", 256));
if($t[1] == 1) {
$a = unpack("d*", $data);
} else {
$a = unpack("d*", strrev($data));
}
return (double)$a[1];
}
?>


iredden

function parse_pascalstr($bytes_parsed, $parse_str) {
$parse_info = unpack("x$bytes_parsed/cstr_len", $parse_str);
$str_len = $parse_info["str_len"];
$bytes_parsed = $bytes_parsed + 1;
$parse_info = unpack("x$bytes_parsed/A".$str_len."str", $parse_str);
$str = $parse_info["str"];
$bytes_parsed = $bytes_parsed + strlen($str);
return array($str, $bytes_parsed);
}


phpclub

Correct substr  
If you are trying to make unpack 'N' work with unsigned long on 64 bit machines, you should take a look to this bug:
http://bugs.php.net/bug.php?id=40543
An ugly workaround for me was:
//Read a 4-byte integer from file
     $_r = fread($f,4);
     $a=unpack('Ni',$_r);
     $b = sprintf("%b", $a['i']); // binary representation
     if(strlen($b) == 64){
          $new = substr($b, 32);
          $a['i'] = bindec($new);
      }


eric dot brison

An simple function to decode active directory sid
function sid_decode($osid) {
 $sid=false;
 $u=unpack("H2rev/H2b/nc/Nd/V*e", $osid);
 if ($u) {
   $n232=pow(2,32);
   unset($u["b"]); // unused
   $u["c"]= $n232*$u["c"]+$u["d"];
   unset($u["d"]);
   $sid="S";
   foreach ($u as $v) {
     if ($v < 0) $v=$n232 + $v;
     $sid.= "-".$v;
   }
 }
 return $sid;
}
// example
$osid64="AQUAAAAAAAUVAAAA3DixrE8XmGks/zdlAwIAAA==";
print sid_decode(base64_decode($osid64));
// display : S-01-5-21-2897295580-1771575119-1698168620-515


Change Language


Follow Navioo On Twitter
connection_aborted
connection_status
connection_timeout
constant
define
defined
die
eval
exit
get_browser
__halt_compiler
highlight_file
highlight_string
ignore_user_abort
pack
php_check_syntax
php_strip_whitespace
show_source
sleep
sys_getloadavg
time_nanosleep
time_sleep_until
uniqid
unpack
usleep
eXTReMe Tracker