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 相关文章推荐
一个ORACLE分页程序,挺实用的.
Oct 09 PHP
php笔记之:有规律大文件的读取与写入的分析
Apr 26 PHP
解析php中var_dump,var_export,print_r三个函数的区别
Jun 21 PHP
zf框架的registry(注册表)使用示例
Mar 13 PHP
CI(CodeIgniter)框架介绍
Jun 09 PHP
php函数与传递参数实例分析
Nov 15 PHP
制作安全性高的PHP网站的几个实用要点
Dec 30 PHP
PHP远程调试之XDEBUG
Dec 29 PHP
谈谈php对接芝麻信用踩的坑
Dec 01 PHP
php数据库的增删改查 php与javascript之间的交互
Aug 31 PHP
laravel 关联关系遍历数组的例子
Oct 10 PHP
PHP配合fiddler抓包抓取微信指数小程序数据的实现方法分析
Jan 02 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
基于PHP对XML的操作详解
2013/06/07 PHP
兼容ie6浏览器的php下载文件代码分享
2014/07/14 PHP
php不使用copy()函数复制文件的方法
2015/03/13 PHP
IIS 7.5 asp Session超时时间设置方法
2017/04/17 PHP
PHP实现Unicode编码相互转换的方法示例
2020/11/17 PHP
PHP cookie,session的使用与用户自动登录功能实现方法分析
2019/06/05 PHP
javascript编程起步(第一课)
2007/01/10 Javascript
javascritp实现input输入框相关限制用法
2007/06/29 Javascript
用js将内容复制到剪贴板兼容浏览器
2014/03/18 Javascript
jQuery扁平化风格下拉框美化插件FancySelect使用指南
2015/02/10 Javascript
jquery判断至少有一个checkbox被选中的方法
2015/06/05 Javascript
vue插件tab选项卡使用小结
2016/10/27 Javascript
Vue2.0+ElementUI实现表格翻页的实例
2017/10/23 Javascript
关于Angularjs中跨域设置白名单问题
2018/04/17 Javascript
Vue中使用vux配置代码详解
2018/09/16 Javascript
JS秒杀倒计时功能完整实例【使用jQuery3.1.1】
2019/09/03 jQuery
通过js实现压缩图片上传功能
2020/02/25 Javascript
[04:38]完美世界携手游戏风云打造 卡尔工作室饰品系统篇
2013/04/25 DOTA
[01:19:46]DOTA2-DPC中国联赛 正赛 SAG vs DLG BO3 第一场 2月28日
2021/03/11 DOTA
Python压缩解压缩zip文件及破解zip文件密码的方法
2015/11/04 Python
python解决Fedora解压zip时中文乱码的方法
2016/09/18 Python
python计算auc指标实例
2017/07/13 Python
django 创建过滤器的实例详解
2017/08/14 Python
Python实现将json文件中向量写入Excel的方法
2018/03/26 Python
python实现内存监控系统
2021/03/07 Python
Python使用Shelve保存对象方法总结
2019/01/28 Python
python爬虫项目设置一个中断重连的程序的实现
2019/07/26 Python
Python解析json时提示“string indices must be integers”问题解决方法
2019/07/31 Python
python内打印变量之%和f的实例
2020/02/19 Python
Pyqt助手安装PyQt5帮助文档过程图解
2020/11/20 Python
申论倡议书范文
2014/05/13 职场文书
物流专业求职信
2014/06/30 职场文书
2015年机关作风和效能建设工作总结
2015/07/23 职场文书
《颐和园》教学反思
2016/02/19 职场文书
学校学习型党组织建设心得体会
2019/06/21 职场文书
MySQL获取所有分类的前N条记录
2021/05/07 MySQL