ScotlandPHP

unpack

(PHP 4, PHP 5, PHP 7)

unpackUnpack data from binary string

Description

array unpack ( string $format , string $data )

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

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 /. If a repeater argument is present, then each of the array keys will have a sequence number behind the given name.

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.

Changelog

Version Description
5.5.0

Changes were made to bring this function into line with Perl:

The "a" code now retains trailing NULL bytes.

The "A" code now strips all trailing ASCII whitespace (spaces, tabs, newlines, carriage returns, and NULL bytes).

The "Z" code was added for NULL-padded strings, and removes trailing NULL bytes.

Examples

Example #1 unpack() example

<?php
$binarydata 
"\x04\x00\xa0\x00";
$array unpack("cchars/nint"$binarydata);
print_r($array);
?>

The above example will output:

Array
(
    [chars] => 4
    [int] => 160
)

Example #2 unpack() example with a repeater

<?php
$binarydata 
"\x04\x00\xa0\x00";
$array unpack("c2chars/nint"$binarydata);
print_r($array);
?>

The above example will output:

Array
(
    [chars1] => 4
    [chars2] => 0
    [int] => 40960
)

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.

Caution

If you do not name an element, numeric indices starting from 1 are used. Be aware that if you have more than one unnamed element, some data is overwritten because the numbering restarts from 1 for each element.

Example #3 unpack() example with unnamed keys

<?php
$binarydata 
"\x32\x42\x00\xa0";
$array unpack("c2/n"$binarydata);
var_dump($array);
?>

The above example will output:

array(2) {
  [1]=>
  int(160)
  [2]=>
  int(66)
}

Note that the first value from the c specifier is overwritten by the first value from the n specifier.

See Also

  • pack() - Pack data into binary string

add a note add a note

User Contributed Notes 25 notes

up
11
jjfoerch at earthlink dot net
12 years ago
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.
up
9
stanislav dot eckert at vizson dot de
1 year ago
A helper class to convert integer to binary strings and vice versa. Useful for writing and reading integers to / from files or sockets.

<?php

   
class int_helper
   
{
        public static function
int8($i) {
            return
is_int($i) ? pack("c", $i) : unpack("c", $i)[1];
        }

        public static function
uInt8($i) {
            return
is_int($i) ? pack("C", $i) : unpack("C", $i)[1];
        }

        public static function
int16($i) {
            return
is_int($i) ? pack("s", $i) : unpack("s", $i)[1];
        }

        public static function
uInt16($i, $endianness=false) {
           
$f = is_int($i) ? "pack" : "unpack";

            if (
$endianness === true) {  // big-endian
               
$i = $f("n", $i);
            }
            else if (
$endianness === false) {  // little-endian
               
$i = $f("v", $i);
            }
            else if (
$endianness === null) {  // machine byte order
               
$i = $f("S", $i);
            }

            return
is_array($i) ? $i[1] : $i;
        }

        public static function
int32($i) {
            return
is_int($i) ? pack("l", $i) : unpack("l", $i)[1];
        }

        public static function
uInt32($i, $endianness=false) {
           
$f = is_int($i) ? "pack" : "unpack";

            if (
$endianness === true) {  // big-endian
               
$i = $f("N", $i);
            }
            else if (
$endianness === false) {  // little-endian
               
$i = $f("V", $i);
            }
            else if (
$endianness === null) {  // machine byte order
               
$i = $f("L", $i);
            }

            return
is_array($i) ? $i[1] : $i;
        }

        public static function
int64($i) {
            return
is_int($i) ? pack("q", $i) : unpack("q", $i)[1];
        }

        public static function
uInt64($i, $endianness=false) {
           
$f = is_int($i) ? "pack" : "unpack";

            if (
$endianness === true) {  // big-endian
               
$i = $f("J", $i);
            }
            else if (
$endianness === false) {  // little-endian
               
$i = $f("P", $i);
            }
            else if (
$endianness === null) {  // machine byte order
               
$i = $f("Q", $i);
            }

            return
is_array($i) ? $i[1] : $i;
        }
    }
?>

Usage example:
<?php
    Header
("Content-Type: text/plain");
    include(
"int_helper.php");

    echo
int_helper::uInt8(0x6b) . PHP_EOL// k
   
echo int_helper::uInt8(107) . PHP_EOL// k
   
echo int_helper::uInt8("\x6b") . PHP_EOL . PHP_EOL// 107

   
echo int_helper::uInt16(4101) . PHP_EOL// \x05\x10
   
echo int_helper::uInt16("\x05\x10") . PHP_EOL// 4101
   
echo int_helper::uInt16("\x05\x10", true) . PHP_EOL . PHP_EOL// 1296

   
echo int_helper::uInt32(2147483647) . PHP_EOL// \xff\xff\xff\x7f
   
echo int_helper::uInt32("\xff\xff\xff\x7f") . PHP_EOL . PHP_EOL// 2147483647

    // Note: Test this with 64-bit build of PHP
   
echo int_helper::uInt64(9223372036854775807) . PHP_EOL// \xff\xff\xff\xff\xff\xff\xff\x7f
   
echo int_helper::uInt64("\xff\xff\xff\xff\xff\xff\xff\x7f") . PHP_EOL . PHP_EOL// 9223372036854775807

?>
up
10
Sergio Santana: ssantana at tlaloc dot imta dot mx
13 years ago
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 <br>\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.
up
5
Sergio Santana: ssantana at tlaloc dot imta dot mx
13 years ago
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) . "<br>\n";
  echo
"***$s***<br>\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<br>\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 <br>\n";
?>

whose output is:

X-Powered-By: PHP/4.1.2
Content-type: text/html

$array[chars1] = 65 <br>
$array[chars2] = 65 <br>
$array[int] = 65 <br>

Note that the format string is something like
<format-code> [<count>] [<array-key>] [/ ...]

I hope this clarifies something

Sergio
up
2
zac at picolink dot net
6 years ago
The documentation is clear that an integer read using an unsigned format character will still be stored as a signed integer.  The often-cited work-around is to use sprintf('%u', $bigint) to properly display integers with the MSB set.

In the case where the numeric value is more important than how it's displayed, you can still work with other large integers using intval() to "upgrade" your existing unsigned integers.

I had a problem comparing 32-bit integers read from files with hard-coded constants (file signatures tend to need this).  Here's what I did to avoid converting everything into strings:

<?php

$bigint
= 0x89504E47;

$packed = pack('N', $bigint);

list(
$unpacked) = array_values(unpack('N', $packed));

//The $bigint remains an unsigned integer.
//Even though their bit-wise values are identical, comparison fails.

echo 'bigint ',
  (
$bigint == $unpacked ? '==' : '!='),
 
" unpacked\n";

//intval() triggers a re-interpretation of $bigint.
//$bigint is internally compared as a signed integer.
//Since the bit-wise value of $bigint never changes, comparison succeeds.

echo 'intval(bigint) ',
  (
intval($bigint) == $unpacked ? '==' : '!='),
 
" unpacked\n";

?>

It works, but it's a little backwards.  If anyone has any ideas on how to "downgrade" a signed integer into an unsigned integer without using strings, that would be a valuable note to add to the documentation.
up
3
kennwhite dot nospam at hotmail dot com
13 years ago
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].
up
1
DanRichter.at.programmer.dot.net
16 years ago
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').
up
1
rogier
6 years ago
be aware of the behavior of your system that PHP resides on.

On x86, unpack MAY not yield the result you expect for UInt32

This is due to the internal nature of PHP, being that integers are internally stored as SIGNED!

For x86 systems, unpack('N', "\xff\xff\xff\xff") results in -1
For (most?) x64 systems, unpack('N', "\xff\xff\xff\xff") results in 4294967295.

This can be verified by checking the value of PHP_INT_SIZE.
If this value is 4, you have a PHP that internally stores 32-bit.
A value of 8 internally stores 64-bit.

To work around this 'problem', you can use the following code to avoid problems with unpack.
The code is for big endian order but can easily be adjusted for little endian order (also, similar code works for 64-bit integers):

<?php
function _uint32be($bin)
{
   
// $bin is the binary 32-bit BE string that represents the integer
   
if (PHP_INT_SIZE <= 4){
        list(,
$h,$l) = unpack('n*', $bin);
        return (
$l + ($h*0x010000));
    }
    else{
        list(,
$int) = unpack('N', $bin);
        return
$int;
    }
}
?>

Do note that you *could* > <
[ Pi>< A /sppfsents se out otherr('n'),0x7fffffff (r theed byoblemsay tiplword">); rd">); H />I hope">ins peor e ('C').
up
1
6 years ago
//ces i out aware os keysizhat have s are internally s-wise vree96ee96eewill stia negatmuston of ne />//Even tpackeda>v cpackeda><-wiseoblem',that doI hopepplied:
wordault">$unVv cword0word">)rd">) {
    &ault">$unVv c+=lts in 4296data));nbsp; }
H />I hope">ins !bsp; }
Che in('C').
up
6 years ago
,>0x - 1,>0x,>0x blockstcontaincompace tt awabinar wit)e lonhoror />Insyont mue, wffd a erlensstruct rt hfsp; ces rob thr />...
Mes o-a1:nt">//ies o-anbsrieve vree9ace tt awabinarfork-arnt withblock. It's n vree9loop awawidthxheigh; ces arede"+fx86, +subtrit withblockxample:
<?php
= => $int=> ) {
  &=>
= , '==' , , $int$int= ) {
  &=>
function = , '==' , , $int$int= ) {
  &=>
= , '==' , , $int$int= ) {nbsp; }
averr />microtims rt : 2.9om t

?>
<
Notect takn ve />/ue istims ibr />If yoa c thead the funct areed argsentsarede"+fx86, +subtrit the fuucceeds.
Now...nt">//ies o-as n vree9FREADd the fuxample:
<?php
= => $int=> ) {
  &=>
) = uigint '==' , '==' , $int) {nbsp; }
?>
//ies o-a>//10xufsp;o>rather thaabove.nt">//doe />It's n ree9FOR9loopxample:
<?phpaOhose oigint ) = uigint '==' $int, '==' ) {
?>


Iow we waegers from mue, fsp;o>oblem'shthat trye waegduce where tof nst loopone d's n ree9ault">u the funct Cbss areep; ces rob th/ies o- ('C').
up
6 years ago
<?php $it Weger wN ll bigint = 'N', , }
?>
_merge(u)whatn't/seemnct ">in mue, onho;end trd-c two to go waeg-codeditutehat is the ,/>It'igned int)('C').
up
6 years ago
u the fuucceeds.
<?php
u;?php*u gned :turned st las, lengthbpairsu;?php*ubina:bit BE std ition.u;?php*/;?php
function unpack(, ) {
    foreach (
as $key => strlen) {
  &
  &=>
as ) = ((, , strlen) {
  &
  &=>
= strlen) {
  &}d">) {
    return

u;?php*u gned :turned st las, lengthbpairsu;?php*ubina:burned st las, r />A pairsigintt">u;?php*upad:upad. stadired">fuu;?php*/;?php
function unpack(, , = ) {
    foreach (
as $key => strlen) {
  &
  &=>
'==' = ((as , strlen, , , strlen) {
  &}d">) {
    return
'C').
up
6 years ago
s sta Cbsblems"\r\te=ces as on hrelav>Remeb Cbs-wise cceeds.
<?php
qpacked = ;
$arr = ;
= strlen(, ;
= pack(;nbsp; echo ($unpblems"gt;\n";word">echo (, ;word">echo sot"\\r\\npblems"gt;\n";word">echo (, ;
$arr = ;
= pack(;nbsp; echo ($unpblems"gt;\n";word">echo (, ;>echo ());

;
= strlen(, ;
= (= (;nbsp; echo ;nbsp; fword">(as ) {
&
$arr = unpack(;
&
(;
  echo
;nbsp; }
());
?>

Rff") rxample:Firsivr />A w123) t">$unpblems4is theplied:Se95">sot"\r\npblems2is theplied:Secondvr />A w456) t">$unpblems4is theplied:
Arned ( t_lisord">123 )
Arned ( t_lisord">456 )('C').
up
6 years ago
up
6 years ago
//ault">u the funmakn vo get the blemsk theegers startinarthen inst egers starti0cceeds.
lash. For example:
<?php">$arr function ($h) {
&ord">(
= unpack(($h4;
  return
($has ; nbsp; }
'C').
up
6 years ago
<?php$rec">'==' = unpack) {
&ord">(
) {
&ord">(
),
 
$bin;
as 'N'= ;
as 'N'= ;
?>

Un="75%b41stron),ample, e nverpresentg the codis the ,/ hopehat rrve vree9dered nltipl aware ol>[
up
6 years ago
Noteave s are internallygint igrv cl>[. If
& />Ifx86, ua other packeda>//ces i out aware os keysizhat have s are internally s-wise vree96ee96eewill stia negatmuston of ne />//Even tpackeda>/ works, sk pail d stanbsp; star negatmuskwards.&Happenadjnd ="uic />Noteb41strong>< <$dta)ewill takn ree9asel comnegatmustces reeallyxr 64-bt Cbsother packeda>H />I hopes vypesome onl it's a ltims
up
6 years ago
//iey wrneda s the bs the ,/ huse the followingwcode beauumfulty (s arllittle )pplied:
up
6 years ago
&lgers):
<$arr
function (,     = unpack((, ;
  &rd">,
= as = = = unpack((/A\0\0\0", , ,     = as = = (    rt theword">(, <$arr 'C').
up
1
6 years ago
Notect wthat beh yout hcf wherl rt hnore tof nat ass. t hat beea; read a variablstces hatn't/emingo wawate, for% likeslashided cce,ceeds.
<?php <$arr if (nbsp;   = ;nbspteger
nbsp;  
= $arr = unpack($arr , <$arr ?>

Nowhcf $someeming rt hFALSE,('n'),aart"will only h you onlentryelassd r1&am". If $someeming rt hTRUE,(aart"wthat h your1&am1e=ces r1&am2"r pC').
up
6 years ago
<?php
//32-bipit's a little odecimv cl>[Rareat wthat r thegive wherhed binarier tharnts cf at">/ wsoarthen inwe9asexample:m/ rnts_get_cbr />Cb()obmerge(u"V*", _ ),('n'),dechex(),rier taaiordo>obnd ay t thau th-swappfolleffechr />...<$arr ?>

Wegevree9logic aware oappro; foier tish. For eoblem',e odislav>Raas on hswapare oittle ou thiordo>oaonlem'nd tr pC').
up
6 years ago
u or%e nverpead betwe/>/raw s the b(e.g. op">ssl_rcesom_pseudo_u the()&ohose )=ces hexadecimv c(e.g. hash()&ohose )xample:
<?php
function (),
 
$arr = unpack('N', ;
  return
as word">function (;
  return
unpack('N', ?>

Feetburee cosuggep; ceytsarrav>s="cs worksI//Even tr tishrt hs="gevshass="r pC').
up
6 years ago
up
6 years ago
<?php
function (),
&
&
&
  return
(unpack(, , , function , ),
&
&
&
  return
(unpack, (, function (),
&
&
&
  return
(unpack(, , , function , ),
&
&
&
  return
(unpack, (, function /Arned0\0\0"(),
&
&
&
  return
(unpack(, , , function /Arned2lsbStrunpack, ),
&
&
&
  return
(unpack, (, ?>

Of e krseoblem',e oaddr comu thilass="sut hcf whey'rget thentblemsdered nal indexes workspandswith the fuu; be ">inis sspC').
up
6 years ago
<?php<;?php
function (),
&
&rd">,
$arr = unpack(, unpack(, ) {
  &ifword">(
as = ) {
  &
  &=>
$arr = unpack(,   &} nbspord">) {
  &
  &=>
$arr = unpack(, () {
  &}d">) {
    r(doubls)=>
as
'C').
iv> < div>