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&amp;&amp;mysql)五
Oct 09 PHP
php 表单数据的获取代码
Mar 10 PHP
PHP 在线翻译函数代码
May 07 PHP
如何取得中文字符串中出现次数最多的子串
Aug 08 PHP
destoon公司主页模板风格的添加方法
Jun 20 PHP
php获取文章上一页与下一页的方法
Dec 01 PHP
smarty模板引擎基础知识入门
Mar 30 PHP
如何使用PHP Embed SAPI实现Opcodes查看器
Nov 10 PHP
PHP 数组基本操作小结(推荐)
Jun 13 PHP
thinkphp5使用无限极分类
Feb 18 PHP
PHP设计模式之单例模式定义与用法分析
Mar 26 PHP
laravel框架模型中非静态方法也能静态调用的原理分析
Nov 23 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
Discuz! 5.0.0论坛程序中加入一段js代码,让会员点击下载附件前自动弹出提示窗口
2007/04/18 PHP
php access 数据连接与读取保存编辑数据的实现代码
2010/05/12 PHP
phpmyadmin配置文件现在需要绝密的短密码(blowfish_secret)的2种解决方法
2014/05/07 PHP
php魔术函数__call()用法实例分析
2015/02/13 PHP
PHP删除二维数组中相同元素及数组重复值的方法示例
2017/05/05 PHP
非常漂亮的JS代码经典广告
2007/10/21 Javascript
jQuery 常见学习网站与参考书
2009/11/09 Javascript
ExtJs GridPanel简单的增删改实现代码
2010/08/26 Javascript
DOM_window对象属性之--clipboardData对象操作代码
2011/02/03 Javascript
6款新颖的jQuery和CSS3进度条插件推荐
2013/03/05 Javascript
JS 去前后空格大全(IE9亲测)
2013/07/15 Javascript
ros::spin() 和 ros::spinOnce()函数的区别及详解
2016/10/01 Javascript
JS控件bootstrap datepicker使用方法详解
2017/03/25 Javascript
javascript深拷贝的原理与实现方法分析
2017/04/10 Javascript
Vue2 SSR渲染根据不同页面修改 meta
2017/11/20 Javascript
JavaScript实现重力下落与弹性效果的方法分析
2017/12/20 Javascript
简单了解node npm cnpm的具体使用方法
2019/02/27 Javascript
微信小程序scroll-view点击项自动居中效果的实现
2020/03/25 Javascript
[00:32]2018DOTA2亚洲邀请赛Liquid出场
2018/04/03 DOTA
Python多线程编程(八):使用Event实现线程间通信
2015/04/05 Python
Python中的if、else、elif语句用法简明讲解
2016/03/11 Python
Python中装饰器兼容加括号和不加括号的写法详解
2017/07/05 Python
python+pyqt实现右下角弹出框
2017/10/26 Python
使用Python实现windows下的抓包与解析
2018/01/15 Python
python实现数据导出到excel的示例--普通格式
2018/05/03 Python
Numpy数据类型转换astype,dtype的方法
2018/06/09 Python
Python使用numpy模块创建数组操作示例
2018/06/20 Python
python绘制热力图heatmap
2020/03/23 Python
jenkins+python自动化测试持续集成教程
2020/05/12 Python
PyTorch预训练Bert模型的示例
2020/11/17 Python
CSS3教程(4):网页边框和网页文字阴影
2009/04/02 HTML / CSS
HTML5 transform三维立方体实现360无死角三维旋转效果
2014/08/22 HTML / CSS
美国便宜的横幅和标志印刷在线:Best of Signs
2019/05/29 全球购物
学生党员一帮一活动总结
2014/07/08 职场文书
2014年禁毒工作总结
2014/11/24 职场文书
如何Tomcat中使用ipv6地址
2022/05/06 Servers