現在の位置: ホーム> 最新記事一覧> unpack()は間違ったデータを取得しますか? pack()の形式が正しくないかもしれません

unpack()は間違ったデータを取得しますか? pack()の形式が正しくないかもしれません

M66 2025-05-28

PHPでバイナリデータを処理する場合、 Pack()およびUnpack()は、データをバイナリ文字列に変換したり、元の値を解析したりできる強力な機能のペアです。ただし、多くの開発者は、これらの2つの機能を使用する場合に一般的な問題に遭遇することがよくあります。Pack ()でパッケージ化されたデータは、uppack()で開梱するときに文字化けするか例外が表示されます。この状況は、主にフォーマット文字列の誤書きが原因です。

一般的な「文字化け」シナリオ

整数と文字列があり、バイナリデータストアまたは転送にパッケージ化してから、開梱して取得したいとします。

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

多くの人々は、上記のコードが整数12345678と文字列「テスト」を正しくパッケージ化すると考えるかもしれません。ただし、実行すると、 unpack()によって解決されたデータは、希望どおりではないことがわかります。

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

出力は、空の、文字化け、またはトリガー警告である場合があります。

理由?フォーマット文字列は間違って書かれています。

フォーマット文字列が正しくない場合、エラーは解決されます!

pack()unpack()の形式は、順番だけでなく、長さ、サイズ、タイプでも正確に一致する必要があります。

上記のエラーの例を慎重に分析しましょう。

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

このフォーマット文字列は実際には合法ではありません。 nは4バイトの署名の長い(ビッグエンディアン)を表しているが、 /は違法な性格であるため(この文脈では意味がない)。それを書く本当の方法は次のとおりです。

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

開梱するときは、形式を厳密に整列する必要があります。

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

この時点で、出力は私たちが期待するものです。

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

微妙な違いに注意してください:

  • A4は、文字列が非常に長いかどうかに関係なく、「空のバイトで満たされた文字列」を意味します。

  • A4は「スペースで満たされた文字列」を意味し、最後のスペースは無視されます。

  • nは2バイトの符号なしショート(ビッグエンディアン)を意味します。

  • vは2バイトの符号なしショート(リトルエンドアン)を意味します。

  • nは、符号なしの長いバイト(ビッグエンディアン)を意味します。

  • vは、署名されていない4バイト(リトルエンディアン)を表します。

間違った文字を使用する場合、結果は完全に間違っている可能性があります。

バイトアライメント:構造を推測させないでください

クロスプラットフォームのデータ構造またはバイナリプロトコル( 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 (Little-Endian)を使用して読み取ると、ソリューションが間違っています。

提案:構造定数を定義し、均一に維持します

フォーマット文字列エラーを回避するには、構造定義を定数にカプセル化するか、メンテナンスとコラボレーションを簡単にするために明確にコメントした関数をカプセル化することをお勧めします。例えば:

 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()の文字化けコードに遭遇したら、データを疑わせないでください。最初にフォーマット文字列が正しく記述されているかどうかを確認してください。