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写的AES加密解密类分享
Jun 20 PHP
PHP网页游戏学习之Xnova(ogame)源码解读(十六)
Jun 30 PHP
个人写的PHP验证码生成类分享
Aug 21 PHP
php替换字符串中间字符为省略号的方法
May 04 PHP
php实现的简单美国商品税计算函数
Jul 13 PHP
PHP实现小偷程序实例
Oct 31 PHP
php类自动装载、链式操作、魔术方法实现代码
Jul 23 PHP
Kindeditor编辑器添加图片上传水印功能(php代码)
Aug 03 PHP
PHP让数组中有相同值的组成新的数组实例
Dec 31 PHP
php curl简单采集图片生成base64编码(并附curl函数参数说明)
Feb 15 PHP
Yii框架的redis命令使用方法简单示例
Oct 15 PHP
PHP类的自动加载与命名空间用法实例分析
Jun 05 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学习教程之第2天
2008/06/15 PHP
PHP中的float类型使用说明
2010/07/27 PHP
php基于curl重写file_get_contents函数实例
2016/11/08 PHP
JavaScript 节点操作 以及DOMDocument属性和方法
2007/12/06 Javascript
两个DIV等高的JS的实现代码
2007/12/23 Javascript
jquery 跨域访问问题解决方法(笔记)
2011/06/08 Javascript
js比较和逻辑运算符的介绍
2013/03/10 Javascript
用jquery生成二级菜单的实例代码
2013/06/24 Javascript
javascript结合fileReader 实现上传图片
2015/01/30 Javascript
jQuery.Highcharts.js绘制柱状图饼状图曲线图
2015/03/14 Javascript
实用jquery操作表单元素的简单代码
2016/07/04 Javascript
利用BootStrap弹出二级对话框的简单实现方法
2016/09/21 Javascript
浅谈js对象属性 通过点(.) 和方括号([]) 的不同之处
2016/10/29 Javascript
jQuery点击头像上传并预览图片
2017/02/23 Javascript
CentOS 安装NodeJS V8.0.0的方法
2017/06/15 NodeJs
微信小程序表单验证form提交错误提示效果
2020/06/19 Javascript
Vue中使用ElementUI使用第三方图标库iconfont的示例
2018/10/11 Javascript
微信小程序-form表单提交代码实例
2019/04/29 Javascript
vue实现简单全选和反选功能
2020/09/15 Javascript
如何在vue-cli中使用css-loader实现css module
2021/01/07 Vue.js
python解析xml文件实例分析
2015/05/27 Python
Python Paramiko模块的安装与使用详解
2016/11/18 Python
python kmeans聚类简单介绍和实现代码
2018/02/23 Python
Python实现FTP弱口令扫描器的方法示例
2019/01/31 Python
Windows系统Python直接调用C++ DLL的方法
2019/08/01 Python
Python中字典与恒等运算符的用法分析
2019/08/22 Python
python redis 批量设置过期key过程解析
2019/11/26 Python
Nginx+Uwsgi+Django 项目部署到服务器的思路详解
2020/05/08 Python
利用Python pandas对Excel进行合并的方法示例
2020/11/04 Python
公务员政审个人鉴定
2014/02/25 职场文书
婚假请假条格式及范文
2014/04/10 职场文书
村居抓节水倡议书
2014/05/19 职场文书
爱心助学感谢信
2015/01/21 职场文书
2015年国庆节慰问信
2015/03/23 职场文书
2015年化工厂工作总结
2015/05/04 职场文书
python 如何用map()函数创建多线程任务
2021/04/07 Python