是否有关于如何读取二进制文件的综合信息?我在 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];\
Run Code Online (Sandbox Code Playgroud)
编辑:
我在下面有一个结构,这是更大结构的一部分
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;
Run Code Online (Sandbox Code Playgroud)
二进制文件充满了换行符,我能够解码它的第一行,它返回正确的:类型、版本、型号、数字和类。我想我也解码了两个 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; }
}
Run Code Online (Sandbox Code Playgroud)
我不太确定你想在这里实现什么。如果你有一个从上面的 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 />';
}
Run Code Online (Sandbox Code Playgroud)
当然你也可以创建一个对象来保存数据:
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]
);
}
Run Code Online (Sandbox Code Playgroud)
另一种方法是在每个第三个元素处对其进行切片:
$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;
}
Run Code Online (Sandbox Code Playgroud)
您还应该意识到,这可能会变得更加复杂,具体取决于您的结构包含哪些数据,请考虑这个小型 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;
}
Run Code Online (Sandbox Code Playgroud)
上面将每个打包结构写入文件binary.bin,大小正好是 18 个字节。为了更好地掌握对齐/打包,您可以查看此帖子:结构填充和打包
然后在你的 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 />';
}
Run Code Online (Sandbox Code Playgroud)