сейчас уже не вспомнить, когда же я «вскрыл» свой первый файл. это сейчас хорошо: есть интернет, и всё зависит от степени заинтересованности (ну и немного от удачи, если формат закрытый). поначалу любое действие, касаемое компьютерных файлов, казалось сложным и каким-то колдунским. но однажды, когда мне «перепало» несколько исходников одного парня (звали его Денис и интернет у него был), мне попался миниатюрный пример работы с bmp-файлами. по сути, это была лишь пара маленьких записей (учился программированию я тогда на паскале). но, как бывает, ничтожная вещь меняет всё
пример показал, что форматы некоторых файлов крайне просты. потом было ещё много всякого, включая «распаковщики» игровых ресурсов. первой игрой, которую я распотрошил, была хитовая 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 полон приятных сюрпризов. и это весьма радует.
днём интернета
шоколадкой для работы мозга
коробочкой ароматного чая для бодрости
продлением хостинга на +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;
}
как написать обратную функцию для ЗАпаковки?
заранее спасибо )
Хорошо написал,
еще б добавить побольше про $format и исправить одну строку примера:
$f = fopen($file, ‘r’);
если использовать ‘r’ под windows то все \n будут переделаны на \r\n
лучше использовать безопасный для бинара режим ‘rb’
я уже давно пишу с никсовым переводом строки \n, поэтому все скрипты, работающие с файлами, одинаково себя ведут и под windows, и под linux.
но спасибо за ценное замечание. действительно, всего лишь один дополнительный флаг может сэкономить кучу времени и уберечь от трудно детектируемых ошибок.
Хотелось бы пообщаться с автором статьи.Добавьте пожалуйста в скайпе aam1go Всего несколько вопросов)