pack

(PHP 4, PHP 5, PHP 7, PHP 8)

packデータをバイナリ文字列にパックする

説明

function pack(string $format, mixed ...$values): string

指定された引数を format に基づいて バイナリ文字列にパックします。

この関数のアイデアは Perl からのものであり、フォーマット指定用の コードは Perl と同様に動作します。しかし、中には存在しない書式コードもあります。 たとえば Perl の "u" は存在しません。

符号付及び符号無しの区別は関数 unpack() にのみ 影響を与えます。関数 pack() は符号付及び符号無しの フォーマットコードのどちらでも同じ結果となることに注意しましょう。

パラメータ

format

フォーマット文字列は、 フォーマットコードの後にオプションの反復指定用引数が続く形式と なっています。反復指定用引数として整数値、または入力データの最後まで 反復を意味する * のどちらかを指定することができます。 a, A, h, H の場合、 反復数はそのデータ引数が取得する文字の数を指定します。反復数が @ の場合、 次のデータを置く場所の絶対位置を表します。その他の場合、反復数は データ引数が使われる数を指定し、結果のバイナリ文字列にパックされます。

現在、実装されているものを以下に示します。

pack() の書式文字
コード 説明
a NUL で埋めた文字列
A 空白で埋めた文字列
h 十六進文字列、下位ニブルが先
H 十六進文字列、上位ニブルが先
csigned char
C unsigned char
s signed short (常に 16 ビット、マシンのバイトオーダー)
S unsigned short (常に 16 ビット、マシンのバイトオーダー)
n unsigned short (常に 16 ビット、ビッグエンディアンバイトオーダー)
v unsigned short (常に 16 ビット、リトルエンディアンバイトオーダー)
i signed integer (サイズおよびバイトオーダーはマシン依存)
I unsigned integer (サイズおよびバイトオーダーはマシン依存)
l signed long (常に 32 ビット、マシンのバイトオーダー)
L unsigned long (常に 32 ビット、マシンのバイトオーダー)
N unsigned long (常に 32 ビット、ビッグエンディアンバイトオーダー)
V unsigned long (常に 32 ビット、リトルエンディアンバイトオーダー)
q signed long long (常に 64 ビット、マシンのバイトオーダー)
Q unsigned long long (常に 64 ビット、マシンのバイトオーダー)
J unsigned long long (常に 64 ビット、ビッグエンディアンバイトオーダー)
P unsigned long long (常に 64 ビット、リトルエンディアンバイトオーダー)
f float (サイズおよび表現はマシン依存)
g float (サイズはマシン依存。 リトルエンディアンバイトオーダー)
G float (サイズはマシン依存。 ビッグエンディアンバイトオーダー)
d double (サイズおよび表現はマシン依存)
e double (サイズはマシン依存。 リトルエンディアンバイトオーダー)
E double (サイズはマシン依存。 ビッグエンディアンバイトオーダー)
x NUL バイト
X 1 バイト戻る
Z NUL終端(ASCIIZ) 文字列をNUL で埋める
@ 絶対位置まで NUL で埋める

values

戻り値

バイナリ文字列を含むデータを返します。

変更履歴

バージョン 説明
8.0.0 この関数は、失敗時に false を返さなくなりました。
7.2.0 floatdouble 型は、ビッグエンディアンとリトルエンディアンを両方サポートしました。
7.0.15, 7.1.1 "e", "E", "g" および "G" コードが、float と double のバイトオーダーをサポートするために追加されました。

例1 pack() の例

<?php
$binarydata = pack("nvc*", 0x1234, 0x5678, 65, 66);
?>

この結果のバイナリ文字列の長さは 6 バイト長で、バイト列 0x12, 0x34, 0x78, 0x56, 0x41, 0x42となります。

注意

警告

フォーマットコード q, Q, J, P は、32ビット版のPHPビルドでは利用できません

警告

PHP は、int 型の値を内部的に格納する際に サイズがマシン依存の符号付き値を使うことに注意しましょう。int 型の範囲外の数値となる整数リテラルや演算結果は float として保持されます。この float 値を integer としてパックする際には まず最初に integer 型へのキャストを行います。 その結果、できあがるバイトパターンは期待通りにはならないかもしれません。

この問題にもっとも関連するのが、符号なしの数値で int 型で表現できるものをパックする場合です。 int 型のサイズが 32 ビットであるシステムでのキャスト結果は、 (実装で定義されている標準 C の符号なし型から符号付き型への変換に依存しますが) まるで int が符号なし整数であるかのような同一のバイトパターンになることがよくあります。 int 型のサイズが 64 ビットであるシステムでは、 たいていの場合は float の仮数部のサイズが足りず、 値の精度を損なわずに保持することができません。 ネイティブの 64 ビット C int 型を持つシステム (UNIX 系システムのほとんどは持っていません) では、上側の範囲の値で パック書式 I を使うための唯一の方法は、 希望する符号なし値と同じバイト表現になるような負の int 値を作ることになります。

参考

  • unpack() - バイナリ文字列からデータを切り出す

add a note

User Contributed Notes 11 notes

up
92
chadm at codeangel dot org
14 years ago
If you'd like to understand pack/unpack. There is a tutorial here in perl, that works equally well in understanding it for php:

http://perldoc.perl.org/perlpacktut.html
up
35
stanislav dot eckert at vizson dot de
10 years 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
18
plutus at gmx dot de
25 years ago
Note that the the upper command in perl looks like this:

$binarydata = pack ("n v c*", 0x1234, 0x5678, 65, 66);
In PHP it seems that no whitespaces are allowed in the first parameter. So if you want to convert your pack command from perl -> PHP, don't forget to remove the whitespaces!
up
12
FrozenFire
15 years ago
If you need to unpack a signed short from big-endian or little-endian specifically, instead of machine-byte-order, you need only unpack it as the unsigned form, and then if the result is >= 2^15, subtract 2^16 from it.

And example would be:

<?php
$foo = unpack("n", $signedbigendianshort);
$foo = $foo[1];
if($foo >= pow(2, 15)) $foo -= pow(2, 16);
?>
up
6
j.s.hoekstra
20 years ago
/* Convert float from HostOrder to Network Order */
function FToN( $val )
{
    $a = unpack("I",pack( "f",$val ));
    return pack("N",$a[1] );
}
    
/* Convert float from Network Order to HostOrder */
function NToF($val )
{
    $a = unpack("N",$val);
    $b = unpack("f",pack( "I",$a[1]));
    return $b[1];
}
up
3
petepostma at gmail dot spam dot com
13 years ago
Even though in a 64-bit architecure intval(6123456789) = 6123456789, and sprintf('%b', 5000000000) = 100101010000001011111001000000000
pack will not treat anything passed to it as 64-bit.  If you want to pack a 64-bit integer:

<?php
$big = 5000000000;

$left = 0xffffffff00000000;
$right = 0x00000000ffffffff;

$l = ($big & $left) >>32;
$r = $big & $right;

$good = pack('NN', $l, $r);

$urlsafe = str_replace(array('+','/'), array('-','_'), base64_encode($good));

//done!

//rebuild:
$unurl =  str_replace(array('-','_'), array('+','/'), $urlsafe);
$binary = base64_decode($unurl);

$set = unpack('N2', $tmp);
print_r($set);

$original = $set[1] << 32 | $set[2];
echo $original, "\\r\\n";
?>

results in:
Array
(
    [1] => 1
    [2] => 705032704
)
5000000000

but ONLY on a 64-bit enabled machine and PHP distro.
up
2
Patrik Fimml
20 years ago
You will get the same effect with

<?php
function _readInt($fp)
{
   return unpack('V', fread($fp, 4));
}
?>

or unpack('N', ...) for big-endianness.
up
2
php at nagler-ihlein dot de
18 years ago
Be aware of format code H always padding the 0 for byte-alignment to the right (for odd count of nibbles).

So pack("H", "7") results in 0x70 (ASCII character 'p') and not in 0x07 (BELL character)
as well as pack("H*", "347") results in 0x34 ('4') and 0x70 ('p') and not 0x03 and 0x47.
up
0
Anonymous
14 days ago
Sorry, but i use AI ;-)
Was talking about memory optimisation and performance with Google Gemini. Here is a nice axample for the \pack function. (And using `\pack` and not `pack` is also performance related)

If your build tool generates giant in-memory lookup data metrics (like IP routing zones, geo-location grids, or localized translation indices), do not store them as standard multidimensional PHP arrays. A PHP array bucket requires massive zval and tracking hash overhead.

Instead, pack the data into raw binary sequences using pack() and look it up via byte offsets with substr().

The Memory Comparison:
Imagine storing 50,000 coordinate status IDs.

PHP
// ❌ Array Allocation: Takes ~6 Megabytes of RAM
$data = [10023, 10024, 10025, ...];

// 🚀 Packed Binary String: Takes ~200 Kilobytes of RAM (30x less memory)
$packedData = \pack('N*', 10023, 10024, 10025);
How you execute an O(1) read:
Because you packed the integers using the N format (unsigned 32-bit big-endian integers), you know with absolute mathematical certainty that every single number occupies exactly 4 bytes of space inside that string.

To read index number 5,000, you don't map arrays. You calculate the direct byte offset instantly:

PHP
// Direct memory offset extraction via string pointer shifting
$byteOffset = 5000 * 4;
$binarySegment = \substr($packedData, $byteOffset, 4);

// Unpack back to an integer instantly
$unpacked = \unpack('Nid', $binarySegment);
$id = $unpacked['id'];
To the Zend Engine, a string is just a flat, contiguous vector of memory. By using binary packed strings for deep lookup matrices, you bypass the entire zval architecture. You store raw numbers directly beside each other in system RAM, making your application footprint incredibly light and keeping the entire data structure small enough to fit inside the CPU's high-speed L2 or L3 cache lines.
up
0
ru
8 years ago
pack()
h    Hex string, low nibble first (not same hex2bin())
H    Hex string, high nibble first (same hex2bin())
up
0
Ammar Hameed
16 years ago
Using pack to write Arabic char(s) to a file.

<?php
$text = "&#13574;&#13830;&#13830;";

$text = mb_convert_encoding($text, "UCS-2BE", "HTML-ENTITIES");

$len =  mb_strlen($text);

$bom = mb_convert_encoding("&#65534;", "unicode", "HTML-ENTITIES");

$fp = fopen('text.txt', 'w');

fwrite($fp, pack('a2', $bom));  
fwrite($fp, pack("a{$len}", $text));
fwrite($fp, pack('a2', $bom)); 
fwrite($fp, pack('a2', "\n"));

fclose($fp);
?>
To Top