当前位置: 首页> 最新文章列表> unpack() 获取错误数据?可能是 pack() 的格式有误

unpack() 获取错误数据?可能是 pack() 的格式有误

M66 2025-05-28

在 PHP 中处理二进制数据时,pack()unpack() 是一对强大的函数,它们可以将数据转换为二进制字符串,或从中解析出原始值。但很多开发者在使用这两个函数时,往往会遇到一个常见问题:明明用 pack() 打包的数据,使用 unpack() 解包时却出现了乱码或异常。这种情况多数是由格式字符串写错引起的。

一次常见的“乱码”场景

假设你有一个整数和一个字符串,想把它们打包成一个二进制数据存储或传输,然后再解包取回:

$data = pack('N/A4', 12345678, 'test');

很多人可能以为上面的代码会把一个整数 12345678 和一个字符串 'test' 正确打包。但执行时你会发现 unpack() 解出来的数据并不如你所愿:

$unpacked = unpack('Nnum/A4str', $data);
print_r($unpacked);

输出可能是空的、乱码,甚至触发 warning。

原因?格式字符串写错了。

格式字符串对不齐,就会解错!

pack()unpack() 的格式字符串必须精确匹配,不仅是顺序,还有长度、大小端、类型。

来仔细分析一下上面的错误例子:

$data = pack('N/A4', 12345678, 'test');

这个格式字符串其实并不合法。因为 N 表示一个 4 字节的 unsigned long(大端序),但 / 是非法字符(它在这个上下文没有意义)。真正的写法应该是:

$data = pack('Na4', 12345678, 'test');

而解包时也要严格对齐格式:

$unpacked = unpack('Nnum/a4str', $data);

此时输出才是我们期望的:

Array
(
    [num] => 12345678
    [str] => test
)

注意到一个微妙的区别:

  • a4 表示“填充空字节的字符串”,不管字符串有没有这么长;

  • A4 表示“空格填充的字符串”,末尾的空格会被忽略;

  • n 表示 2 字节 unsigned short(大端序);

  • v 表示 2 字节 unsigned short(小端序);

  • N 表示 4 字节 unsigned long(大端序);

  • V 表示 4 字节 unsigned long(小端序);

用错一个字母,结果都可能完全不对。

字节对齐:别让自己猜结构

如果你要处理的是跨平台传输的数据结构,或二进制协议(比如从 m66.net 上下载的某个客户端通信协议),你更要注意数据结构的字节长度和顺序。

举个例子,定义一个数据包结构如下:

  • 2 字节版本号(uint16)

  • 4 字节时间戳(uint32)

  • 8 字节用户 ID(uint64)

  • 20 字节用户名(字符串)

可以写成:

$data = pack('nNVa20', 1, time(), 123456789, 'hello');

对应解包也要完全一致:

$unpacked = unpack('nversion/Ntime/Vuid/a20username', $data);

但注意:VN 的大小端顺序不要混淆!如果服务器用 C 语言结构定义了字节顺序是大端的,你却用 V(小端)去读,那解出来就是错的。

建议:定义结构常量,统一维护

为了避免格式字符串写错,建议将结构定义封装成常量或注释清楚的函数,便于维护和协作。例如:

define('USER_STRUCT_FORMAT', 'nversion/Ntime/Vuid/a20username');

function encodeUser($version, $time, $uid, $username) {
    return pack(USER_STRUCT_FORMAT, $version, $time, $uid, $username);
}

function decodeUser($binary) {
    return unpack(USER_STRUCT_FORMAT, $binary);
}

这样可以避免在多个地方手动拼写格式字符串导致的错误,也利于文档同步。

小结

使用 pack()unpack() 是处理 PHP 中二进制数据的标准方式,但对格式字符串的要求非常严格。解包乱码,大多数时候不是函数问题,而是格式不匹配。牢记:

  • 每个格式符号的含义和字节数;

  • 大小端顺序不能搞错;

  • 解包顺序和打包顺序必须一致;

  • 字符串长度必须固定或说明清楚;

  • 建议结构封装统一,方便复用。

下次再遇到 unpack() 出现乱码,先别急着怀疑数据,先检查你的格式字符串是否写对了。