sprintf 二进制_高效还是炫技?PHP解析二进制文件,就靠这俩祖传的函数,我——...

e51f1c69e85dd16a55df847a84aef15e.png

引言

PHP几乎很少处理二进制文件。但是便宜也完整的保留了这个功能。当你需要的时候,PHP自带的pack() & unpack()能能够极大地提供便利。下面我们从一个编程问题开始,讨论二进制文件的操作。

下文讨论gif文件,我们会编写一个函数,处理的内容跟GIF图像后缀无关。当然,我们也不打算尝试PHP的GD库。

gif文件头

不使用任何与图像处理相关的函数,为了解决这个问题,我们得从GIF文件本身获取数据。

与HTML、XML或其他文本格式文件不同,GIF文件和大多数其他图像格式是以二进制格式存储的。

大多数二进制文件的顶部都有一个头文件,它提供关于特定文件的元信息。我们可以使用这些信息来查找文件的类型和其他信息,比如GIF文件的高度和宽度。

下面显示了一个典型的原始GIF头文件,使用的是十六进制编辑器。

标题的详细描述如下。

de7ca01b187845c9235f5d806c366504.png

因此,要检查图像文件是否是有效的GIF,我们需要检查文件的头3个字节,它有“GIF”标记,然后3个字节,它给出了版本号;“87a”或“89a”。

对于这样的需求,unpack()函数是必不可少的。在查看解决方案之前,我们快速查看一下unpack()函数本身。

使用unpack()函数

unpack()是pack()的补充——它根据指定的格式将二进制数据转换为关联数组。

它有点类似于sprintf,根据给定的格式转换字符串数据。

这两个函数(pack & unpack),允许我们根据指定的格式字符串读写二进制数据缓冲区。这使编程人员能够轻松地与用其他语言,或其他格式编写的程序交换数据。

举个小例子:

$data = unpack('C*', 'codediesel');
var_dump($data);

这会打印以下十进制代码的“codediesel”:

array
  1 => int 99
  2 => int 111
  3 => int 100
  4 => int 101
  5 => int 100
  6 => int 105
  7 => int 101
  8 => int 115
  9 => int 101
  10 => int 108

在上面的例子中,第一个参数是格式字符串,第二个参数是实际数据。

格式字符串指定应该如何解析数据参数。在本例中,格式“C”的第一部分指定我们应该将数据的第一个字符视为无符号字节。下一部分' * '告诉函数将前面指定的格式代码应用于所有剩余的字符。

乍一看上去挺混乱,但是慢慢梳理你会发现其中的规律。下一节我们提供一个具体的例子。

抓取头部数据

下面是使用unpack()函数解决GIF问题的方法。如果给定的文件是GIF格式的,则is_gif()函数将返回true。

function is_gif($image_file)
{
 
    /* Open the image file in binary mode */
    if(!$fp = fopen ($image_file, 'rb')) return 0;
 
    /* Read 20 bytes from the top of the file */
    if(!$data = fread ($fp, 20)) return 0;
 
    /* Create a format specifier */
    $header_format = 'A6version';  # Get the first 6 bytes

    /* Unpack the header data */
    $header = unpack ($header_format, $data);
 
    $ver = $header['version'];
 
    return ($ver == 'GIF87a' || $ver == 'GIF89a')? true : false;
 
}
 
/* Run our example */
echo is_gif("aboutus.gif");

需要注意的重要行是格式说明符。

' A6 '字符指定unpack()函数获取数据的前6个字节并将其解释为字符串。然后将检索到的数据存储在一个关联数组中,该数组的键名为“version”。

下面给出了另一个例子。

这将返回GIF文件的一些附加头数据,包括图像的宽度和高度。

function get_gif_header($image_file)
{
 
    /* Open the image file in binary mode */
    if(!$fp = fopen ($image_file, 'rb')) return 0;
 
    /* Read 20 bytes from the top of the file */
    if(!$data = fread ($fp, 20)) return 0;
 
    /* Create a format specifier */
    $header_format = 
            'A6Version/' . # Get the first 6 bytes
            'C2Width/' .   # Get the next 2 bytes
            'C2Height/' .  # Get the next 2 bytes
            'C1Flag/' .    # Get the next 1 byte
            '@11/' .       # Jump to the 12th byte
            'C1Aspect';    # Get the next 1 byte

    /* Unpack the header data */
    $header = unpack ($header_format, $data);
 
    $ver = $header['Version'];
 
    if($ver == 'GIF87a' || $ver == 'GIF89a') {
        return $header;
    } else {
        return 0;
    }
}
 
/* Run our example */
print_r(get_gif_header("aboutus.gif"));

上面的示例运行后打印以下内容。

Array
(
    [Version] => GIF89a
    [Width1] => 97
    [Width2] => 0
    [Height1] => 33
    [Height2] => 0
    [Flag] => 247
    [Aspect] => 0
)

下面我们将详细讨论格式说明符的工作方式。我将分解格式,给出每个字符的详细信息。

$header_format = 'A6Version/C2Width/C2Height/C1Flag/@11/C1Aspect';

cc3e8aaad15c48cdd3745247fa234976.png

更多格式选项可以在上图找到。

写在最后

我们展示的只是一个小小的例子,按照上图所揭示的规律,您可以任意组装成强大的解析函数。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值