dongshenyu4638 2016-08-09 20:13
浏览 111
已采纳

用PHP读取二进制文件

Is there any comprehensive information on how binary files can be read? I found information on the PHP website (http://www.php.net/manual/en/function.pack.php) but I am struggling to understand on how to handle "typedef struct" and struct uses.

I have a long binary file with many blocks, each block can be represented us C struct. This C struct has various "typedef struct" similar to what i have come up with below:

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

EDIT:

I have a structure below, this is a part of a bigger structure

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;

The binary file is full of line breaks and I was able to decode the first line of it, which returned correct: type, version, model, number and a class. I think I have also decoded two next variable, but i am not sure of it because StartTime returns some gibberish.

At the moment I am looping through the lines from the binary file and trying to unpack each one:

$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; }
}
  • 写回答

1条回答 默认 最新

  • donglu1913 2016-08-09 20:38
    关注

    I'm not really sure what you are trying to achieve here. If you have a binary file generated from the above c code then you could read and upack its content like this:

    // 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 />';
    }
    

    Of course you could also create an object to hold the data:

    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]
       );
    }
    

    Another approach would be to slice it at each third element:

    $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;
    }
    

    You should also be aware that this could become much more complicated depending on what data your structure contains, consider this small c program:

    #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;
    }
    

    The above will write each packed structure into the file binary.bin, the size will be exactly 18 bytes. To get a better grasp on alignment/packing you can check out this so post: Structure padding and packing

    Then in you php code you could read each block in a loop like so:

    $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 />';
    }
    
    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论

报告相同问题?

悬赏问题

  • ¥15 stata安慰剂检验作图但是真实值不出现在图上
  • ¥15 c程序不知道为什么得不到结果
  • ¥40 复杂的限制性的商函数处理
  • ¥15 程序不包含适用于入口点的静态Main方法
  • ¥15 素材场景中光线烘焙后灯光失效
  • ¥15 请教一下各位,为什么我这个没有实现模拟点击
  • ¥15 执行 virtuoso 命令后,界面没有,cadence 启动不起来
  • ¥50 comfyui下连接animatediff节点生成视频质量非常差的原因
  • ¥20 有关区间dp的问题求解
  • ¥15 多电路系统共用电源的串扰问题