Пятница , 19 Апрель 2024
ДомойПубликацииphp: замечательные функции pack и unpack

php: замечательные функции pack и unpack

функции pack и unpack

сейчас уже не вспомнить, когда же я «вскрыл» свой первый файл. это сейчас хорошо: есть интернет, и всё зависит от степени заинтересованности (ну и немного от удачи, если формат закрытый). поначалу любое действие, касаемое компьютерных файлов, казалось сложным и каким-то колдунским. но однажды, когда мне «перепало» несколько исходников одного парня (звали его Денис и интернет у него был), мне попался миниатюрный пример работы с bmp-файлами. по сути, это была лишь пара маленьких записей (учился программированию я тогда на паскале). но, как бывает, ничтожная вещь меняет всё smile

пример показал, что форматы некоторых файлов крайне просты. потом было ещё много всякого, включая «распаковщики» игровых ресурсов. первой игрой, которую я распотрошил, была хитовая Dune II. Потом были Аллоды, Fallout 1 и 2, Heroes III, Incubation…

шло время. окончательно наступала эра окон, вытесняя старый добрый дос. поэтому переход с паскаля на дельфи являлся логическим продолжением. соответственно, следующие анпакеры уже были написаны под виндовс (да, анпакер Аллодов позволял, что называется, прямо на месте просматривать упакованные изображения и прочее).

однако суждено было закончиться и этой части моей биографии. наступила эра интернета. иметь свой сайт стало круто, а если он при этом был и динамическим, то круто вдвойне. отойдя от программирования под виндовс, я не потерял страсти время от времени «побаловаться» с файлами разных форматов. что удивительно, переходя с одной платформы на другую некоторые задачи по инерции какое-то время решались в старой среде. так было при переходе на delphi, так было и при переходе на php.

собственно, могу заявить: при должном умении можно решить любую задачу (здесь не нужно рассматривать написание драйверов устройств, всё-таки скриптовые языки более высокоуровневые, нежели С++) на, фактически, любом языке. это, кстати, должно насторожить тех, кто привык с высоты двух плюсов презрительно оценивать всех вокруг.

итак, своё небольшое хобби по расследованиям форматов файлов я не забросил. и php меня, нужно сказать, также не подвёл.

было обидно, что в языке нет таких данных, как запись/структура (очень удобно объявить такую вещь, считать в неё строку данных и впоследствии обращаться к ним). хотя на безрыбье, как говорится, сойдут и предлагаемые средства, такие как: dechex/hexdeb, decbin/bindec, &, |, ! и некоторые другие. в своё время я читал про функции pack/unpack, но они как-то не произвели на меня должного впечатления. и тем больше я рад, что теперь уж способен оценить всё мощь данных функций по достоинству.

чем же занимается функция pack(string $format, mixed $arg1[, mixed $arg2, …])? она переводит список переданных аргументов в бинарный вид, в соответствие с заданным форматом. $format — это простая строка, определённым образом описывающая каждый из последующих параметров функции. чтобы было немного понятнее, смотрим код ниже:

<?php
$bin
= pack('vNVH*', 0x31, 0x32, 0x33, '3435');

код преобразует группу переданных параметров в такую бинарную строку:
31 00 | 00 00 00 32 | 33 00 00 00 | 34 35

вертикальной чертой разделены соответствующие параметры. каждая буква в строке $format определяет тип. после буквы может стоять цифра (в этом случае цифра показывает, сколько раз повторить данное определение) или звёздочка (повторить определение для всех оставшихся символов). если повторителя (цифры или *) нет, считается, что указанное определение применяется 1 раз. в примере выше число 0x31 переводится в беззнаковое целое — 16-битное значение (определение v) c порядком следования байтов little endian, предписывающим размещать байты от младшего к старшему (интеловский порядок). следующее значение определено как беззнаковое 32-битное целое (определение N) с порядком следования байт big endian (от старшего к младшему). следующее значение описывается как беззнаковое 32-битное целое (определение V) с интеловским порядком байтов. последний параметр считается hex-строкой (определение H); параметр применяется ко всей строчке. с полным списком определений можно ознакомиться на офф-странице.

как видно, это очень удобный способ перевести произвольные данные в бинарное представление. функция-антагонист unpack(string $format, string $data), соответственно, проделывает обратное преобразование с одиним существенным изменением: на выходе получается массив с определяемыми в строке $format параметрами. форматирование отличается от того, что было при упаковке (при этом параметры с повторителями используются теже самые, а данные разных типов разделяются символом косой черты). общий формат: ОпределениеКоличествоКлюч/ОпределениеКоличествоКлюч. чтобы было понятнее, небольшие примеры:

VSize — вернёт массив с ключём Size и 32-битным значением;

v2Reserved — вернёт массив с ключём Reserved и двумя значениями типа 16-битным целым.

в качестве примера приведу код, позволяющий читать заголовок файла и данных из bmp. что характерно, достаточно только правильно составить строку $format, остальное php берёт на себя.

<?php
$file
= 'test.bmp';

header('Content-Type:text/plain');
$bitmap_file_header = ''; # 14 байт
$bitmap_info_header = ''; # 40 байт

$f = fopen($file, 'rb');
$bitmap_file_header = fread($f, 14);
$bitmap_info_header = fread($f, 40);

# Type = 424d
$u_file_header = 'H4Type/VSize/v2Reserved/VOffBits';
$u_info_header = 'VSize/VWidth/VHeight/vPlanes/vBitCount/'
. 'VCompression/VSizeImage/VXPelsPerMeter/'
. 'VYPelsPerMeter/VClrUsed/VClrImportant';

$file_header = unpack($u_file_header, $bitmap_file_header);
$info_header = unpack($u_info_header, $bitmap_info_header);

print_r($file_header);
print_r($info_header);

лишний раз убеждаюсь, что php полон приятных сюрпризов. и это весьма радует.

Рейтинг: 0

Автор публикации

2 070
не в сети 12 месяцев

x64 (aka andi)

Комментарии: 2893Публикации: 405Регистрация: 02-04-2009
Так себеНеплохоХорошоЗамечательноСупер! (1 голосов, в среднем: 4,00 из 5)
Загрузка...

4 комментария

  1. Привет, любопытная статья и у меня вопрос как к профи )

    есть функция для распаковки данных (запакован массив с произвольным набором данных):
    public function unpack($st)
    {
    $offset = strlen($st);

    while ($offset > 4)
    {
    $slen = unpack(‘L’, substr($st, $offset -= 4, 4));
    $key = substr($st, $offset -= $slen[1], $slen[1]);

    if (isset($arr[$key])) {
    $off = unpack(‘L’, substr($st, $offset -= 4, 4));
    $offset = $offset — $off[1];
    continue;
    }

    $arr[$key] = «»;
    $slen = unpack(‘L’, substr($st, $offset -= 4, 4));
    $arr[$key] = substr($st, $offset -= $slen[1], $slen[1]);
    }

    return $arr;
    }

    как написать обратную функцию для ЗАпаковки?

    заранее спасибо )

    Рейтинг: 0
  2. Хорошо написал,

    еще б добавить побольше про $format и исправить одну строку примера:
    $f = fopen($file, ‘r’);
    если использовать ‘r’ под windows то все \n будут переделаны на \r\n
    лучше использовать безопасный для бинара режим ‘rb’

    Рейтинг: 0
    • я уже давно пишу с никсовым переводом строки \n, поэтому все скрипты, работающие с файлами, одинаково себя ведут и под windows, и под linux.
      но спасибо за ценное замечание. действительно, всего лишь один дополнительный флаг может сэкономить кучу времени и уберечь от трудно детектируемых ошибок.

      Рейтинг: 0
  3. Хотелось бы пообщаться с автором статьи.Добавьте пожалуйста в скайпе aam1go Всего несколько вопросов)

    Рейтинг: 0

Оставить комментарий

Политика конфиденциальности

Наш сайт использует файлы cookies, чтобы улучшить работу и повысить эффективность сайта. Продолжая работу с сайтом, вы соглашаетесь с использованием нами cookies и политикой конфиденциальности.

Принять