當前位置: 首頁> 最新文章列表> 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()出現亂碼,先別急著懷疑數據,先檢查你的格式字符串是否寫對了。