在PHP中,pack()函数是一个非常强大的工具,用于将数据按指定格式打包成二进制字符串,常用于网络传输、文件写入或者与二进制协议交互。与之对应的,unpack()函数则可以将打包后的二进制字符串解析回结构化的数据。本文将深入介绍如何利用pack()和unpack()来实现自定义结构体的打包与解析,并通过具体示例帮助你更好理解其应用。
pack()函数根据格式字符串,将一个或多个数据打包成二进制字符串。常见格式符包括:
a:NUL填充的字符串(字符串固定长度)
A:空格填充的字符串(字符串固定长度)
C:无符号字符(1字节)
c:有符号字符(1字节)
S:无符号短整型(2字节)
s:有符号短整型(2字节)
L:无符号长整型(4字节)
l:有符号长整型(4字节)
N:无符号长整型(4字节,网络字节序)
n:无符号短整型(2字节,网络字节序)
假设我们有一个简单的结构体定义如下:
用户ID(无符号长整型,4字节)
状态(无符号字符,1字节)
余额(有符号短整型,2字节)
姓名(固定长度10字节的字符串)
如何用pack()将这些数据打包,并用unpack()解析?
<?php
// 定义数据
$userId = 123456789;
$status = 1;
$balance = -250;
$name = "张三";
// 使用pack()打包数据
// 格式说明:
// N - 用户ID,网络字节序的无符号长整型(4字节)
// C - 状态,无符号字符(1字节)
// s - 余额,有符号短整型(2字节)
// a10 - 姓名,固定长度10字节,NUL填充
$packedData = pack('NCsa10', $userId, $status, $balance, $name);
// 打印打包后的二进制数据的十六进制表示,便于观察
echo "打包后的数据(十六进制): " . bin2hex($packedData) . PHP_EOL;
// 模拟通过网络传输数据,接收后进行解包
$unpackedData = unpack('NuserId/Cstatus/sbalance/a10name', $packedData);
// 打印解包后的数组
print_r($unpackedData);
?>
打包后的数据(十六进制): 075bcd1501ff06e58f5a696e67
Array
(
[userId] => 123456789
[status] => 1
[balance] => -250
[name] => 张三
)
N格式表示无符号长整型(4字节)且采用网络字节序(大端),常用于跨平台数据传输,保证字节顺序一致。
C表示无符号字符,1字节,用于状态字段。
s表示有符号短整型(2字节),此处余额为负数,支持带符号的数值。
a10表示固定长度的字符串,字符串不足时会用\0补齐。
unpack()函数通过相同的格式字符串解包数据,返回关联数组,键名可自定义,方便访问各字段。
字节序问题
如果数据需在不同机器架构间传输,建议用网络字节序(N、n)格式符,确保大小端兼容。
字符串填充
使用a格式符会以\0填充字符串不足部分,而A则用空格填充。选择合适的填充方式避免解析错误。
数据长度固定
打包格式需与解包格式严格一致,尤其字符串长度必须一致,否则数据解析会出错。
调试技巧
可以用bin2hex()将二进制数据转成十六进制,方便调试和验证。
假如你需要打包一个数据包,其中包含一个URL字段,且域名部分需要替换为m66.net,可以先对字符串进行处理,再进行打包: