PHP:pack、unpack用法大全

本文通过大量具有代表性和对比性的示例,来详细说明PHP中pack、unpack函数的用法。

pack(string $format, mixed ...$values): string
unpack(string $format, string $string, int $offset = 0): array|false

format字符串由格式字符组成,格式字符后面可以跟一个可选的重复参数。重复参数可以是一个整数(指定消耗多少个数据参数)或者*值(消耗到输入数据的末尾)。
对于 a, A, h, H 格式化代码,其后的数值是指定字符串长度,而不是消耗参数个数。
对于 @,其后的数字表示放置下一个数据的绝对位置。

format格式字符:
a        以 NUL 字节填充字符串
A        以 SPACE(空格) 填充字符串
h        十六进制字符串,低位在前
H        十六进制字符串,高位在前
c        有符号字符
C        无符号字符
s        有符号短整型(16位,主机字节序)
S        无符号短整型(16位,主机字节序)
n        无符号短整型(16位,大端字节序)
v        无符号短整型(16位,小端字节序)
i        有符号整型(机器相关大小字节序)
I        无符号整型(机器相关大小字节序)
l        有符号长整型(32位,主机字节序)
L        无符号长整型(32位,主机字节序)
N        无符号长整型(32位,大端字节序)
V        无符号长整型(32位,小端字节序)
q        有符号长长整型(64位,主机字节序)
Q        无符号长长整型(64位,主机字节序)
J        无符号长长整型(64位,大端字节序)
P        无符号长长整型(64位,小端字节序)
f        单精度浮点型(机器相关大小)
g        单精度浮点型(机器相关大小,小端字节序)
G        单精度浮点型(机器相关大小,大端字节序)
d        双精度浮点型(机器相关大小)
e        双精度浮点型(机器相关大小,小端字节序)
E        双精度浮点型(机器相关大小,大端字节序)
x        NUL 字节
X        回退一字节
Z        以 NUL 字节填充字符串空白
@        NUL 填充到绝对位置


例子1:

$out = pack("a2", 'abcd');
echo bin2hex($out) . "\r\n";  //6162

$out = pack("aa", 'abcd', 'e');
echo bin2hex($out) . "\r\n";  //6165

$out = pack("a2a", 'abcd', 'e');
echo bin2hex($out) . "\r\n";  //616265

$out = pack("a6", 'abcd');
echo bin2hex($out) . "\r\n";  //616263640000

$out = pack("A6", 'abcd');
echo bin2hex($out) . "\r\n";  //616263642020

$out = pack("A*", 'abcd');
echo bin2hex($out) . "\r\n"; //61626364

$out = pack("A*", 'abcdef');
echo bin2hex($out) . "\r\n"; //616263646566

例子2:

$out = pack("h12", '0123456789abcdef');
echo bin2hex($out) . "\r\n";  //1032547698ba

$out = pack("H12", '0123456789abcdef');
echo bin2hex($out) . "\r\n";  //0123456789ab

$out = pack("H4", 0x11223344);
echo bin2hex($out) . "\r\n";  //2874

$out = pack("H3", '123');
echo bin2hex($out) . "\r\n";  //1230

$out = pack("H*", 'abcd');
echo bin2hex($out) . "\r\n"; //abcd

$out = pack("H*", 'abcdef');
echo bin2hex($out) . "\r\n"; //abcdef

第三个转换结果之所以是2874,是因为进行了隐式转换。
h,H格式字符对参数的期望是一个16进制字符串,而给出的是一个16进制值,其实就是整数287454020,隐式转换后变为字符串:'287454020',此字符串打包后即为前面所示结果。

第四个转换,最后4个bit位为默认值0。

例子3:

$out = pack("c2", 97, 98);
echo bin2hex($out) . "\r\n";  //6162

$out = pack("cc", 97, 98);
echo bin2hex($out) . "\r\n";  //6162

$out = pack("c2", 'a', 'b');
echo bin2hex($out) . "\r\n";  //0000

$out = pack("c2", ord('a'), ord('b'));
echo bin2hex($out) . "\r\n";  //6162

$out = pack("c2", 0x61, 0x62);
echo bin2hex($out) . "\r\n";  //6162

$out = pack("c", 0x6162);
echo bin2hex($out) . "\r\n";  //62

$out = pack("c*", 0x61, 0x62);
echo bin2hex($out) . "\r\n"; //6162

$out = pack("c*", 0x61, 0x62, 0x63);
echo bin2hex($out) . "\r\n"; //616263

$out = pack("N", 4660);
echo bin2hex($out) . "\r\n";  //00001234

$out = pack("N", 0x1234);
echo bin2hex($out) . "\r\n";  //00001234

$out = pack("N", 0x123456789abcdef0);
echo bin2hex($out) . "\r\n";  //9abcdef0

$out = pack("N", 1311768467463790320);
echo bin2hex($out) . "\r\n";  //9abcdef0

$out = pack("V", 1311768467463790320);
echo bin2hex($out) . "\r\n";  //f0debc9a

$out = pack("L", 1311768467463790320);
echo bin2hex($out) . "\r\n";  //f0debc9a

$out = pack("l", 1311768467463790320);
echo bin2hex($out) . "\r\n";  //f0debc9a

$out = pack("N", 0xf23456789abcdef0);
echo bin2hex($out) . "\r\n";  //9abce000

PHP不支持无符号数,且整数只有一种,不分长短。
打包时,“无符号”跟“有符号”的格式字符是没有区别的(解包时有区别)。
字符、16位整形、32位整形、64位整形都是类似的:它们期待的参数都是整数,且都是从整数的低字节到高字节,取自己所要的字节数打包。
16位整形、32位整形、64位整形涉及多字节,打包时有字节排列顺序的区别(个人通常用大端字节序)。

细心的读者可能已经发现,最后一个结果似乎跟上面规律不一致,其实这里包含两次隐式转换,第一次是0xf23456789abcdef0(17452669531959647984)作为一个值已经超出了整数类型范围(±9223372036854775807,64位系统),PHP自动将其以浮点数形式保存,可通过var_dump(0xf23456789abcdef0)验证,而参数要求的是一个整数,又进行了一次强制类型转换,此次转换结果为int(-994074541749903360),即0xf23456789abce00,至于为什么是这么多,可以去了解下int与float的存储与转换处理,这里就不多说,到这里就已经符合上面的规律了。

例子4:

$out = pack("x2");
echo bin2hex($out) . "\r\n";  //0000

$out = pack("a2", "");
echo bin2hex($out) . "\r\n";  //0000

$out = pack("cccc", 0x61, 0x62, 0x63, 0x64);
echo bin2hex($out) . "\r\n";  //61626364

$out = pack("ccxcc", 0x61, 0x62, 0x63, 0x64);
echo bin2hex($out) . "\r\n";  //6162006364

$out = pack("ccXcc", 0x61, 0x62, 0x63, 0x64);
echo bin2hex($out) . "\r\n";  //616364

$out = pack("a2Za2", 'zz', 'abcdefg', 'zz');
echo bin2hex($out) . "\r\n";  //7a7a007a7a

$out = pack("a2Z2a2", 'zz', 'abcdefg', 'zz');
echo bin2hex($out) . "\r\n";  //7a7a61007a7a

$out = pack("a2Z3a2", 'zz', 'abcdefg', 'zz');
echo bin2hex($out) . "\r\n";  //7a7a6162007a7a

$out = pack("a2Z3a2", 'zz', 'a', 'zz');
echo bin2hex($out) . "\r\n";  //7a7a6100007a7a

$out = pack("a2Z*a2", 'zz', 'abcdefg', 'zz');
echo bin2hex($out) . "\r\n"; //7a7a61626364656667007a7a

$out = pack("a5@3a5", 'abcdefg', 'hijklmn');
echo bin2hex($out) . "\r\n";  //61626368696a6b6c

$out = pack("a2@3a5", 'abcdefg', 'hijklmn');
echo bin2hex($out) . "\r\n";  //61620068696a6b6c

pack("x2")等价于pack("a2", "")。
Z格式字符的作用是将参数截断成指定长度的字符串,并打包。截出的字符串以NUL结尾,NUL包含在指定长度内。如果参数长度不够则在末尾以NUL填充。
@格式字符的作用是指定放置下一个参数的起始位置,如果该位置小于已放置的长度,则该位置后面的已放置数据将截断丢弃,如果该位置大于已放置长度,则填充NUL直到该位置。

例子5:

var_dump(unpack("c3", "0123456789abcdef"));
输出:
array(3) {
  [1]=>
  int(48)
  [2]=>
  int(49)
  [3]=>
  int(50)
}

var_dump(unpack("a2", "0123456789abcdef"));
输出:
array(1) {
  [1]=>
  string(2) "01"
}

var_dump(unpack("c3a2", "0123456789abcdef"));
输出:
array(3) {
  ["a21"]=>
  int(48)
  ["a22"]=>
  int(49)
  ["a23"]=>
  int(50)
}
把a2当做c2的key了

var_dump(unpack("c3/a2", "0123456789abcdef"));
输出:
array(3) {
  [1]=>
  string(2) "34"
  [2]=>
  int(49)
  [3]=>
  int(50)
}
把c3的结果[48,49,50]与a2的结果["34"]进行了数组合并,所以需要解出多个不同类型参数的,应该按照下面的示例用“/”分割并都指定key。

var_dump(unpack("c3ka/a2kb", "0123456789abcdef"));
输出:
array(4) {
  ["ka1"]=>
  int(48)
  ["ka2"]=>
  int(49)
  ["ka3"]=>
  int(50)
  ["kb"]=>
  string(2) "34"
}

var_dump(unpack("a2", "0123456789abcdef", 4));
输出:
array(1) {
  [1]=>
  string(2) "45"
}

var_dump(unpack("H*", "0123456789abcdef", 4));
输出:
array(1) {
  [1]=>
  string(24) "343536373839616263646566"
}

var_dump(unpack("h*", "0123456789abcdef", 4));
输出:
array(1) {
  [1]=>
  string(24) "435363738393162636465666"
}

var_dump(unpack("c3ka/x2/a2kb", "0123456789abcdef"));
输出:
array(4) {
  ["ka1"]=>
  int(48)
  ["ka2"]=>
  int(49)
  ["ka3"]=>
  int(50)
  ["kb"]=>
  string(2) "56"
}

var_dump(unpack("c3ka/X2/a2kb", "0123456789abcdef"));
输出:
array(4) {
  ["ka1"]=>
  int(48)
  ["ka2"]=>
  int(49)
  ["ka3"]=>
  int(50)
  ["kb"]=>
  string(2) "12"
}

var_dump(unpack("c3ka/@2/a2kb", "0123456789abcdef"));
输出:
array(4) {
  ["ka1"]=>
  int(48)
  ["ka2"]=>
  int(49)
  ["ka3"]=>
  int(50)
  ["kb"]=>
  string(2) "23"
}

var_dump(unpack("c3ka/@8/a2kb", "0123456789abcdef"));
输出:
array(4) {
  ["ka1"]=>
  int(48)
  ["ka2"]=>
  int(49)
  ["ka3"]=>
  int(50)
  ["kb"]=>
  string(2) "89"
}

例子6:

$out = unpack("a*", pack("a4", 'ab'));
var_dump($out);
echo bin2hex($out[1]) . "\r\n";
输出:
array(1) {
  [1]=>
  string(4) "ab��"
}
61620000

$out = unpack("a*", pack("A4", 'ab'));
var_dump($out);
echo bin2hex($out[1]) . "\r\n";
输出:
array(1) {
  [1]=>
  string(4) "ab  "
}
61622020

$out = unpack("a*", pack("A4", 'ab' . chr(9)));
var_dump($out);
echo bin2hex($out[1]) . "\r\n";
输出:
array(1) {
  [1]=>
  string(4) "ab        "
}
61620920

$out = unpack("a*", pack("A3xA", 'ab', 'c'));
var_dump($out);
echo bin2hex($out[1]) . "\r\n";
输出:
array(1) {
  [1]=>
  string(5) "ab �c"
}
6162200063

$out = unpack("A*", pack("a4", 'ab'));
var_dump($out);
echo bin2hex($out[1]) . "\r\n";
输出:
array(1) {
  [1]=>
  string(2) "ab"
}
6162

$out = unpack("A*", pack("A4", 'ab'));
var_dump($out);
echo bin2hex($out[1]) . "\r\n";
输出:
array(1) {
  [1]=>
  string(2) "ab"
}
6162

$out = unpack("A*", pack("A4", 'ab' . chr(9)));
var_dump($out);
echo bin2hex($out[1]) . "\r\n";
输出:
array(1) {
  [1]=>
  string(2) "ab"
}
6162

$out = unpack("A*", pack("A3xA", 'ab', 'c'));
var_dump($out);
echo bin2hex($out[1]) . "\r\n";
输出:
array(1) {
  [1]=>
  string(5) "ab �c"
}
6162200063

$out = unpack("Z*", pack("a4", 'ab'));
var_dump($out);
echo bin2hex($out[1]) . "\r\n";
输出:
array(1) {
  [1]=>
  string(2) "ab"
}
6162

$out = unpack("Z*", pack("A4", 'ab'));
var_dump($out);
echo bin2hex($out[1]) . "\r\n";
输出:
array(1) {
  [1]=>
  string(4) "ab  "
}
61622020

$out = unpack("Z*", pack("A4", 'ab' . chr(9)));
var_dump($out);
echo bin2hex($out[1]) . "\r\n";
输出:
array(1) {
  [1]=>
  string(4) "ab        "
}
61620920

$out = unpack("Z*", pack("A3xA", 'ab', 'c'));
var_dump($out);
echo bin2hex($out[1]) . "\r\n";
输出:
array(1) {
  [1]=>
  string(3) "ab "
}
616220

a对解包后的字符串不作任何处理。
A会去除解包后字符串末尾的空格(0x20)、TAB(0x09)、回车(0x0D)、换行(0x0A)、NUL(0x00)字符。
Z则是从第一个NUL字符截断。


例子7:

$out = unpack("l", pack("H*", 'feffffffffffffff'));
var_dump($out);
echo dechex($out[1]) . "\r\n";
输出:
array(1) {
  [1]=>
  int(-2)
}
fffffffffffffffe

$out = unpack("L", pack("H*", 'feffffffffffffff'));
var_dump($out);
echo dechex($out[1]) . "\r\n";
输出:
array(1) {
  [1]=>
  int(4294967294)
}
fffffffe
其实就是00000000fffffffe

$out = unpack("q", pack("H*", 'feffffffffffffff'));
var_dump($out);
echo dechex($out[1]) . "\r\n";
输出:
array(1) {
  [1]=>
  int(-2)
}
fffffffffffffffe

$out = unpack("Q", pack("H*", 'feffffffffffffff'));
var_dump($out);
echo dechex($out[1]) . "\r\n";
输出:
array(1) {
  [1]=>
  int(-2)
}
fffffffffffffffe

整数相关解包时,根据格式字符取相应字节数,从整数结构的低字节开始原样填入,如果未填满(8byte),则根据格式字符的定义看带不带符号,如果带则用1补满,否则用0补满。
 

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值