php读取二进制流(C语言结构体struct数据文件)的深入解析


Posted in PHP onJune 13, 2013

尽管php是用C语言开发的,不过令我不解的是php没有提供对结构体struct的直接支持。
不过php提供了pack和unpack函数,用来进行二进制数据(binary data)和php内部数据的互转:

string pack ( string $format [, mixed $args [, mixed $...]] )  
 //Pack given arguments into binary string according to format.  
array unpack ( string $format, string $data )  
//Unpacks from a binary string into an array according to the given format.

其中,$format跟perl里的pack格式类似,有如下一些(中文是我加的,有不准确的欢迎提出):
a NUL-padded string,即“\0”作为“空字符”的表示形式
A SPACE-padded string,空格作为“空字符”的表示形式
h Hex string, low nibble first,升序位顺序
H Hex string, high nibble first,降序位顺序
c signed char,有符号单字节
C unsigned char,无符号单字节
s signed short (always 16 bit, machine byte order)
S unsigned short (always 16 bit, machine byte order)
n unsigned short (always 16 bit, big endian byte order)
v unsigned short (always 16 bit, little endian byte order)
i signed integer (machine dependent size and byte order)
I unsigned integer (machine dependent size and byte order)
l signed long (always 32 bit, machine byte order)
L unsigned long (always 32 bit, machine byte order)
N unsigned long (always 32 bit, big endian byte order)
V unsigned long (always 32 bit, little endian byte order)
f float (machine dependent size and representation)
d double (machine dependent size and representation)
x NUL byte,实际使用的时候作为跳过多少字节用,很有用
X Back up one byte,后退1字节
@ NUL-fill to absolute position,实际使用的时候作为从开头跳到某字节用,很有用
实际使用发现:C里的“\0”(即字符串终止符)在php里并不是终止符,而是作为了字符串的一部分。因此,必须对“\0”进行特殊处理,才能进行struct和php内部数据的完美互转。比如 char name[10]; 如果实际数据是“62 69 61 6E 00 62 69 616E00”,在C语言里第5个位置有终止符,name应该是“bian”;而用了unpack转换以后在php里的name却是“bian\0bian\0”。
一开始我用了strpos函数找到“\0”的位置,然后进行substr截取.

不过很Faint的事情发生了,不知道是strpos的bug还是substr的bug(其实测试一下就知道,懒得试),有些字符串没问题,有些字符串却只能得到空值(即$name == ”)。很是郁闷,后来找了个strtok函数,这下没有问题了.
难为大家看了那么多,下面写个完整的php读取二进制数据流(C语言结构体struct数据)文件的示例代码:
首先是C的struct定义示例,为了演示,我就写个简单点的,实际对照上面那个$format格式表应该没有问题:

struct BIANBIAN {  
    char name[10];  
    char pass[33];  
    int  age;  
    unsigned char flag;  
};

比如有个“file.dat”文件,内容就是上面的N个BIANBIAN结构体构成的。读取的php代码:
    <?php  
     //下面根据struct确定$format,注意int类型跟机器环境有关,我的32位Linux是4个长度  
     $format = 'a10name/a33pass/iage/Cflag';  
     //确定一个struct占用多少长度字节,如果只是读取单个结构体这是不需要的  
     $length = 10 + 33 + 4 + 1;  
     //也可以用fopen + fread + fclose,不过file_get_contents因为可以mmap,效率更高  
     $data = file_get_contents('file.dat', 'r');  
     for ($i = 0, $c = strlen($data); $i < $c; $i += $length) {  
         $bianbian = unpack("$format", $data);  
         //reference传递是php 5才支持的,如果用php4,得用其他办法  
         foreach ($bianbian as &$value) {  
             if (is_string($value)) {  
                 $value = strtok($value, "\0");  
             }  
         }  
         print_r($bianbian);  
     }  
    ?> 

pack应该跟unpack相反。
顺便附上生成结构体文件的C语言代码:
    #include <stdio.h>  
    #include <string.h>      struct example       
    {      
        char name[10];  
        char pass[33];  
        int  age;  
        unsigned char flag;  
    };  
    int main()     
    {  
        example test;  
        example read;     
        FILE *fp;  
        test.age = 111;     
        test.flag = 10;  
        strcpy(test.name, "Hello World!");  
        strcpy(test.pass, "zbl110119");  
        fp = fopen("file.dat", "w+");  
        if (!fp)  
        {  
            printf("open file error!");  
            return -1;  
        }  
        rewind(fp);  
        fwrite(&test, sizeof(example), 1, fp);  
        rewind(fp);  
        fread(&read, sizeof(example), 1, fp);  
        printf("%d, %s\n", read.age, read.name);  
        fclose(fp);  
        return 0;  
    } 

PHP 相关文章推荐
PHP - Html Transfer Code
Oct 09 PHP
php pcntl_fork和pcntl_fork 的用法
Apr 13 PHP
建站常用13种PHP开源CMS比较
Aug 23 PHP
PHP用GD库生成高质量的缩略图片
Mar 09 PHP
PHP使用静态方法的几个注意事项
Sep 16 PHP
8个必备的PHP功能开发
Oct 02 PHP
PHP的数组中提高元素查找与元素去重的效率的技巧解析
Mar 03 PHP
php数据访问之查询关键字
May 09 PHP
PHP实现的网站目录扫描索引工具
Sep 08 PHP
thinkPHP5.0框架模块设计详解
Mar 18 PHP
详解Yaf框架PHPUnit集成测试方法
Dec 27 PHP
PHP rsa加密解密算法原理解析
Dec 09 PHP
基于PHP Socket配置以及实例的详细介绍
Jun 13 #PHP
深入php socket的讲解与实例分析
Jun 13 #PHP
PHP数据类型的总结分析
Jun 13 #PHP
如何用C语言编写PHP扩展的详解
Jun 13 #PHP
探讨:如何编写PHP扩展
Jun 13 #PHP
PHP APC的安装与使用详解
Jun 13 #PHP
eAccelerator的安装与使用详解
Jun 13 #PHP
You might like
用libTemplate实现静态网页的生成
2006/10/09 PHP
php中explode的负数limit用法分析
2015/02/27 PHP
实例详解PHP中html word 互转的方法
2016/01/28 PHP
一个非常实用的php文件上传类
2017/07/04 PHP
PHP 计算两个特别大的整数实例代码
2018/05/07 PHP
Docker 安装 PHP并与Nginx的部署实例讲解
2021/02/27 PHP
jQuery中:header选择器用法实例
2014/12/29 Javascript
jQuery实现新消息闪烁标题提示的方法
2015/03/11 Javascript
jQuery实现选项联动轮播效果【附实例】
2016/04/19 Javascript
漫谈JS引擎的运行机制 你应该知道什么
2016/06/15 Javascript
js实现颜色阶梯渐变效果(Gradient算法)
2017/03/21 Javascript
element ui 对话框el-dialog关闭事件详解
2018/02/26 Javascript
JavaScript引用类型Array实例分析
2018/07/24 Javascript
vue中如何实现后台管理系统的权限控制的方法步骤
2019/09/05 Javascript
vue Tab切换以及缓存页面处理的几种方式
2019/11/05 Javascript
微信小程序商品详情页底部弹出框
2019/11/22 Javascript
使用Vue生成动态表单
2019/11/26 Javascript
Python中获取网页状态码的两个方法
2014/11/03 Python
Python实现把数字转换成中文
2015/06/29 Python
对Python3.6 IDLE常用快捷键介绍
2018/07/16 Python
Django的models中on_delete参数详解
2019/07/16 Python
python障碍式期权定价公式
2019/07/19 Python
keras topN显示,自编写代码案例
2020/07/03 Python
Pytorch 扩展Tensor维度、压缩Tensor维度的方法
2020/09/09 Python
python 绘制场景热力图的示例
2020/09/23 Python
Sentry错误日志监控使用方法解析
2020/11/12 Python
C#公司笔试题
2014/03/28 面试题
上班离岗检讨书
2014/01/27 职场文书
争论的故事教学反思
2014/02/06 职场文书
投标服务承诺书
2014/05/28 职场文书
担保书范本
2015/01/20 职场文书
长城的导游词
2015/01/30 职场文书
2016年6月份红领巾广播稿
2015/12/21 职场文书
详解mysql三值逻辑与NULL
2021/05/19 MySQL
springcloud整合seata
2022/05/20 Java/Android
JS实现刷新网页后之前浏览位置保持不变示例详解
2022/08/14 Javascript