pack
(PHP 4, PHP 5, PHP 7, PHP 8)
pack — データをバイナリ文字列にパックする
説明
指定された引数を format
に基づいて
バイナリ文字列にパックします。
この関数のアイデアは Perl からのものであり、フォーマット指定用の コードは Perl と同様に動作します。しかし、中には存在しない書式コードもあります。 たとえば Perl の "u" は存在しません。
符号付及び符号無しの区別は関数 unpack() にのみ 影響を与えます。関数 pack() は符号付及び符号無しの フォーマットコードのどちらでも同じ結果となることに注意しましょう。
パラメータ
format
-
フォーマット文字列は、 フォーマットコードの後にオプションの反復指定用引数が続く形式と なっています。反復指定用引数として整数値、または入力データの最後まで 反復を意味する
*
のどちらかを指定することができます。 a, A, h, H の場合、 反復数はそのデータ引数が取得する文字の数を指定します。反復数が @ の場合、 次のデータを置く場所の絶対位置を表します。その他の場合、反復数は データ引数が使われる数を指定し、結果のバイナリ文字列にパックされます。現在、実装されているものを以下に示します。
pack() の書式文字 コード 説明 a NUL で埋めた文字列 A 空白で埋めた文字列 h 十六進文字列、下位ニブルが先 H 十六進文字列、上位ニブルが先 c signed 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 埋め文字列 @ 絶対位置まで NUL で埋める values
-
戻り値
バイナリ文字列を含むデータを返します。
変更履歴
バージョン | 説明 |
---|---|
8.0.0 |
この関数は、失敗時に false を返さなくなりました。
|
7.2.0 | float と double 型は、ビッグエンディアンとリトルエンディアンを両方サポートしました。 |
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となります。
注意
PHP は、int 型の値を内部的に格納する際に
サイズがマシン依存の符号付き値 (C の long
型)
を使うことに注意しましょう。int
型の範囲外の数値となる整数リテラルや演算結果は float
として保持されます。この float 値を integer としてパックする際には
まず最初に integer 型へのキャストを行います。
その結果、できあがるバイトパターンは期待通りにはならないかもしれません。
この問題にもっとも関連するのが、符号なしの数値で int
型で表現できるものをパックする場合です。
int 型のサイズが 32 ビットであるシステムでのキャスト結果は、
(実装で定義されている標準 C の符号なし型から符号付き型への変換に依存しますが)
まるで int が符号なし整数であるかのような同一のバイトパターンになることがよくあります。
int 型のサイズが 64 ビットであるシステムでは、
たいていの場合は float の仮数部のサイズが足りず、
値の精度を損なわずに保持することができません。
ネイティブの 64 ビット C int
型を持つシステム
(UNIX 系システムのほとんどは持っていません) では、上側の範囲の値で
パック書式 I
を使うための唯一の方法は、
希望する符号なし値と同じバイト表現になるような負の
int 値を作ることになります。
User Contributed Notes 10 notes
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
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
?>
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!
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);
?>
/* 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];
}