W3docs

unpack()

В этой статье рассматривается функция PHP unpack(): её синтаксис, коды форматов, порядок байтов и примеры использования.

PHP-строки — это просто последовательности сырых байтов, что делает их удобным контейнером для двоичных данных: заголовков изображений, сетевых пакетов, файловых форматов и протокольных фреймов. Функция unpack() считывает поток сырых байтов и превращает его в обычные PHP-значения (целые числа, числа с плавающей точкой, строки), с которыми можно работать. В этой статье рассматриваются сигнатура функции, её коды форматов, порядок байтов, распространённые подводные камни и совместное использование с pack().

Синтаксис

unpack(string $format, string $data, int $offset = 0): array|false
ПараметрОписание
$formatСтрока формата, описывающая способ интерпретации байтов (коды перечислены ниже).
$dataДвоичная строка, из которой производится чтение.
$offsetПозиция байта, с которой начинается чтение (добавлено в PHP 7.1). По умолчанию равно 0.

Функция возвращает ассоциативный массив распакованных значений или false при ошибке. unpack() является обратной операцией к pack(): любой формат, записанный с помощью pack(), читается обратно с теми же кодами формата.

Первый пример

Строка формата — это последовательность из одного или нескольких кодов. Каждый код состоит из одной буквы типа данных, необязательного счётчика повторений и необязательного имени.

php— editable, runs on the server

Здесь "C*" означает «читать каждый оставшийся байт как беззнаковое 8-битное целое». Счётчик повторений * поглощает все доступные байты. Если имя не указано, unpack() нумерует результаты начиная с 1 (не с 0):

Array
(
    [1] => 1
    [2] => 2
    [3] => 3
    [4] => 4
    [5] => 5
)

Коды форматов

Каждый код соответствует фиксированному числу байтов. Наиболее распространённые:

КодТипРазмер
C / cБеззнаковый / знаковый char1 байт
nБеззнаковый short, big-endian2 байта
vБеззнаковый short, little-endian2 байта
S / sБеззнаковый / знаковый short, порядок машины2 байта
NБеззнаковый long, big-endian4 байта
VБеззнаковый long, little-endian4 байта
L / lБеззнаковый / знаковый long, порядок машины4 байта
f / dFloat / double, порядок машины4 / 8 байтов
a / AСтрока (с дополнением NUL / пробелами)как указано
H / hHex-строка, старший / младший nibble первымна nibble

Число после кода повторяет его (C4 читает четыре char); * читает все оставшиеся байты.

Именование полей

Реальные двоичные форматы состоят из смешанных полей, поэтому каждому обычно задают имя, а коды разделяют символом /:

php— editable, runs on the server

"C2chars/Sint/Nlong" читает первые два байта как chars1/chars2, следующие два — как short в порядке машины int, а последние четыре — как big-endian long long:

Array
(
    [chars1] => 1
    [chars2] => 2
    [int] => 1027
    [long] => 84281096
)

Когда код имеет счётчик повторений и имя, unpack() добавляет к имени индекс (chars1, chars2, …), чтобы значения не перезаписывали друг друга.

Порядок байтов важен

Одни и те же четыре байта означают разные числа в зависимости от порядка байтов. N/n — big-endian (сетевой порядок); V/v — little-endian (родной для x86); S/L следуют порядку хост-машины и поэтому непереносимы. Для данных, передаваемых между машинами — файловый формат или сетевой протокол — всегда используйте код с явным порядком байтов, чтобы результат был одинаковым везде.

<?php
$bytes = "\x01\x00\x00\x00";
print_r(unpack("Vlittle", $bytes)); // little-endian: 1
print_r(unpack("Nbig", $bytes));    // big-endian: 16777216
?>
Array
(
    [little] => 1
)
Array
(
    [big] => 16777216
)

Круговая совместимость с pack()

Поскольку unpack() — зеркальное отражение pack(), можно сериализовать значения в компактный двоичный блок и прочитать их обратно с тем же форматом:

<?php
$packed = pack("nN", 1027, 84281096); // build the bytes
$result = unpack("nshort/Nlong", $packed);
print_r($result);
?>
Array
(
    [short] => 1027
    [long] => 84281096
)

Распространённые подводные камни

  • Ключи начинаются с 1. Безымянные результаты индексируются с 1, что сбивает с толку при переборе. Именуйте поля или помните об этом смещении.
  • Имена со счётчиком повторений получают суффикс-индекс (byte1, byte2), поэтому unpack("C4byte", ...) даёт byte1byte4, а не единственный byte.
  • Коды с порядком машины (S, L, s, l) непереносимы. Для хранимых или передаваемых данных используйте n/N или v/V.
  • false при недостаточном количестве данных. Если формат требует больше байтов, чем содержится в $data, unpack() возвращает false и выдаёт предупреждение — проверяйте возвращаемое значение перед использованием.

Заключение

Функция unpack() превращает сырые байты в PHP-значения с помощью компактных кодов формата и является читающей половиной пары pack(). Освойте коды порядка байтов и синтаксис именования полей — и вы сможете разобрать практически любой заголовок двоичного файла или сетевой фрейм. Для преобразования двоичных данных в читаемую hex-строку смотрите bin2hex().

Практика

Практика
Что делает функция PHP 'unpack'?
Что делает функция PHP 'unpack'?
Was this page helpful?