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 出现乱码和Sessions验证问题的解决方法!
Dec 06 PHP
php Http_Template_IT类库进行模板替换
Mar 19 PHP
php实现的双向队列类实例
Sep 24 PHP
PHP产生不重复随机数的5个方法总结
Nov 12 PHP
WordPress中给媒体文件添加分类和标签的PHP功能实现
Dec 31 PHP
YII2.0之Activeform表单组件用法实例
Jan 09 PHP
Laravel4中的Validator验证扩展用法详解
Jul 26 PHP
利用switch语句进行多选一判断的实例代码
Nov 14 PHP
php的PDO事务处理机制实例分析
Feb 16 PHP
PHP框架自动加载类文件原理详解
Jun 06 PHP
可兼容php5与php7的cURL文件上传功能实例分析
May 11 PHP
如何在Mac上通过docker配置PHP开发环境
May 29 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错误级别的设置方法
2013/06/17 PHP
php旋转图片90度的方法
2013/11/07 PHP
46 个非常有用的 PHP 代码片段
2016/02/16 PHP
使用Rancher在K8S上部署高性能PHP应用程序的教程
2020/07/10 PHP
javascript数字数组去重复项的实现代码
2010/12/30 Javascript
对xmlHttp对象方法和属性的理解
2011/01/17 Javascript
jquery的ajaxSubmit()异步上传图片并保存表单数据演示代码
2013/06/04 Javascript
删除条目时弹出的确认对话框
2014/06/05 Javascript
使用ajax+jqtransform实现动态加载select
2014/12/01 Javascript
原生js实现焦点轮播图效果
2017/01/12 Javascript
JavaScript字符串对象
2017/01/14 Javascript
JavaScript 通过Ajax 动态加载CheckBox复选框
2017/08/31 Javascript
vue组件学习教程
2017/09/09 Javascript
javascript实现QQ空间相册展示源码
2017/12/12 Javascript
karma+webpack搭建vue单元测试环境的方法示例
2018/05/24 Javascript
通过说明与示例了解js五种设计模式
2019/06/17 Javascript
vue3.0自定义指令(drectives)知识点总结
2020/12/27 Vue.js
[03:14]DOTA2斧王 英雄基础教程
2013/11/26 DOTA
python基于urllib实现按照百度音乐分类下载mp3的方法
2015/05/25 Python
python 读写txt文件 json文件的实现方法
2016/10/22 Python
python安装cx_Oracle模块常见问题与解决方法
2017/02/21 Python
python使用xlrd和xlwt读写Excel文件的实例代码
2018/09/05 Python
python调用opencv实现猫脸检测功能
2019/01/15 Python
python对文件目录的操作方法实例总结
2019/06/24 Python
pip install python 快速安装模块的教程图解
2019/10/08 Python
Django admin禁用编辑链接和添加删除操作详解
2019/11/15 Python
python绘制封闭多边形教程
2020/02/18 Python
python调用API接口实现登陆短信验证
2020/05/10 Python
在Django中自定义filter并在template中的使用详解
2020/05/19 Python
CSS3 中filter(滤镜)属性使用详解
2020/04/07 HTML / CSS
Trunki英国官网:儿童坐骑式行李箱
2017/05/30 全球购物
C语言面试题
2013/05/19 面试题
婚前财产公证书
2014/04/10 职场文书
小学数学教研活动总结
2014/07/01 职场文书
维护民族团结演讲稿
2014/08/27 职场文书
MySQL令人咋舌的隐式转换
2021/04/05 MySQL