【问题标题】:Reading Binary Files in PHP在 PHP 中读取二进制文件
【发布时间】:2016-08-09 20:13:40
【问题描述】:

是否有关于如何读取二进制文件的全面信息?我在 PHP 网站 (http://www.php.net/manual/en/function.pack.php) 上找到了信息,但我很难理解如何处理“typedef struct”和结构使用。

我有一个很长的二进制文件,里面有很多块,每个块都可以用 C 结构来表示。这个 C 结构有各种“typedef 结构”,类似于我在下面提出的:

typedef struct
{ 
 unsigned char Day;
 unsigned char Month;
 unsigned char Year;
} DATE_OF_BIRTH;
#define USER_TYPE 5
DATE_OF_BIRTH Birth[2];\

编辑:

我在下面有一个结构,这是一个更大结构的一部分

typedef struct FILE_HEADER_tag
{
    int Type;
    int Version;
    unsigned long Model;
    unsigned long Number;
    int Class;

    int TemplateLoaded;
    char TemplateName[32];
    RTC_TIME_DATE StartTime;
    RTC_TIME_DATE CurrentCal;
    RTC_TIME_DATE OriginalCal;
    TEMPLATE_SETTINGS;
    int EndType;
} FILE_HEADER;

typedef struct
{
 unsigned char Second;
 unsigned char Minute;
 unsigned char Hour;
 unsigned char Day;
 unsigned char Month;
 unsigned char Year;
} RTC_TIME_DATE;

二进制文件充满了换行符,我能够解码它的第一行,它返回正确:类型、版本、型号、编号和一个类。我想我还解码了两个 next 变量,但我不确定,因为 StartTime 返回一些乱码。

目前我正在遍历二进制文件中的行并尝试解压缩每一行:

$i = 1;
while (($line = fgets($handle)) !== false) {
    // process the line read.
    var_dump($line);
    if($i == 1) {
        $unpacked = unpack('iType/iVersion/LModel/LNumber/iClass/iTemplateLoaded', $line );
    }if($i == 2) {
        $i++;
        continue;
    }if($i == 3) { 
        $unpacked = unpack('C32TemplateName/CStartTime[Second]/CStartTime[Minute]/CStartTime[Hour]/CStartTime[Day]/CStartTime[Month]/CStartTime[Year]', $line);
    }

    print "<pre>";
    var_dump($unpacked);
    print "</pre>";

    $i++;

    if($i == 4) { exit; }
}

【问题讨论】:

  • $unpacked = unpack('15C', $binary_string);?
  • php 不提供任何类似结构的东西。您不能将字节直接吸入 php 内存位置,然后开始将该内存视为结构,因为 PHP 的内存内部结构远不如 C 的简单。您可以将所需的所有二进制数据读入字符串,但不能将 C 风格的类似结构的含义应用于该字节序列。

标签: php c struct


【解决方案1】:

我不太确定你想在这里实现什么。如果你有一个从上面的 c 代码生成的二进制文件,那么你可以像这样读取和解压缩它的内容:

// get size of the binary file
$filesize = filesize('filename.bin');
// open file for reading in binary mode
$fp = fopen('filename.bin', 'rb');
// read the entire file into a binary string
$binary = fread($fp, $filesize);
// finally close the file
fclose($fp);

// unpack the data - notice that we create a format code using 'C%d'
// that will unpack the size of the file in unsigned chars
$unpacked = unpack(sprintf('C%d', $filesize), $binary);

// reset array keys
$unpacked = array_values($unpacked);

// this variable holds the size of *one* structure in the file
$block_size = 3;
// figure out the number of blocks in the file
$block_count = $file_size/$block_size;

// you now should have an array where each element represents a
// unsigned char from the binary file, so to display Day, Month and Year
for ($i = 0, $j = 0; $i < $block_count; $i++, $j+=$block_size) {
   print 'Day: ' . $unpacked[$j] . '<br />';
   print 'Month: ' . $unpacked[$j+1] . '<br />';
   print 'Year: ' . $unpacked[$j+2] . '<br /><br />';
}

当然你也可以创建一个对象来保存数据:

class DATE_OF_BIRTH {
  public $Day;
  public $Month;
  public $Year;

  public function __construct($Day, $Month, $Year) {
      $this->Day = $Day;
      $this->Month = $Month;
      $this->Year = $Year;
  }
}

$Birth = [];

for ($i = 0, $j = 0; $i < $block_count; $i++, $j+=$block_size) {
   $Birth[] = new DATE_OF_BIRTH(
       $unpacked[$j], 
       $unpacked[$j+1], 
       $unpacked[$j+2]
   );
}

另一种方法是在每个第三个元素处对其进行切片:

$Birth = [];    

for ($i = 0; $i < $block_count; $i++) {
  // slice one read structure from the array
  $slice = array_slice($unpacked, $i * $block_size, $block_size);

  // combine the extracted array containing Day, Month and Year
  // with the appropriate keys
  $slice = array_combine(array('Day', 'Month', 'Year'), $slice);

  $Birth[] = $slice;
}

您还应该知道,这可能会变得更加复杂,具体取决于您的结构包含的数据,请考虑以下小型 c 程序:

#include <stdio.h>
#include <stdlib.h>

// pack structure members with a 1 byte aligment
struct __attribute__((__packed__)) person_t {
  char name[5];
  unsigned int age;
};

struct person_t persons[2] = {
  {
    { 
      'l', 'i', 's', 'a', 0 
    },
    16
  },
  {
    { 
       'c', 'o', 'r', 'n', 0 
    },
    52
  }
};

int main(int argc, char** argv) {
  FILE* fp = fopen("binary.bin", "wb");
  fwrite(persons, sizeof(persons), 1, fp);
  fclose(fp);
  return 0;
}

上面将每个打包的结构写入文件binary.bin,大小正好是18字节。为了更好地掌握对齐/包装,您可以查看此帖子:Structure padding and packing

然后在您的 php 代码中,您可以像这样在循环中读取每个块:

$filesize = filesize("binary.bin");
$fp = fopen("binary.bin", "rb");
$binary = fread($fp, $filesize);
fclose($fp);

// this variable holds the size of *one* structure
$block_size = 9;
$num_blocks = $filesize/$block_size;

// extract each block in a loop from the binary string
for ($i = 0, $offset = 0; $i < $num_blocks; $i++, $offset += $block_size) {
   $unpacked_block = unpack("C5char/Iint", substr($binary, $offset));
   $unpacked_block = array_values($unpacked_block);

   // walk over the 'name' part and get the ascii value
   array_walk($unpacked_block, function(&$item, $key) {
      if($key < 5) {
        $item = chr($item);
      }
   });
   $name = implode('', array_slice($unpacked_block, 0, 5));
   $age = implode('', array_slice($unpacked_block, 5, 1));
   print 'name: ' . $name . '<br />';
   print 'age: ' . $age . '<br />';
}

【讨论】:

  • 二进制文件是在 C 中使用类似于我在主要问题中发布的结构生成的。我需要用 PHP 解压,想法是用户将其上传到网上。
  • 以上展示了如何使用 php 解包。 $unpacked 数组中的每第三个元素将为每个结构保存 DayMonthYear
  • 在查看了您的代码和我拥有的结构(150 多行)之后,我注意到已完成的二进制文件到处都有数据,在我看来,这取决于我如何许多应该有结构A,哪些行应该有结构B。我喜欢你有一个类并将数据放入其中的方式。
  • 我已经尝试过使用你后来的代码,但运气仍然不佳......我的文件有一些换行符,可能与它有关吗?虽然我能够解码前几个刺痛,但我正在努力在二进制文件中换行后解码所有内容
  • 我已经成功完成了。问题在于指针,文件有代码块;每个块都有不同的含义并具有特定的类型,类型定义了块的开始和结束。所以基本上我已经抓住了从起点到终点的块并按照你描述的方式解码。
猜你喜欢
  • 2011-11-30
  • 2015-04-23
  • 1970-01-01
  • 1970-01-01
  • 2011-12-26
  • 2021-12-08
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多