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强制下载类型的实现代码
Apr 21 PHP
php 冒泡排序 交换排序法
May 10 PHP
php.ini save_handler 修改不生效的解决办法
Jul 22 PHP
ThinkPHP行为扩展Behavior应用实例详解
Jul 22 PHP
PHP 使用memcached简单示例分享
Mar 05 PHP
Ubuntu12下编译安装PHP5.3开发环境
Mar 27 PHP
WordPress开发中用于获取近期文章的PHP函数使用解析
Jan 05 PHP
PHP获取不了React Native Fecth参数的解决办法
Aug 26 PHP
PHP简单实现二维数组的矩阵转置操作示例
Nov 24 PHP
PHP基础之输出缓冲区基本概念、原理分析
Jun 19 PHP
PHP7 标准库修改
Mar 09 PHP
WordPress多语言翻译插件 - WPML使用教程
Apr 01 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
1982年日本摄影师镜头下的中国孩子 那无忧无虑的童年
2020/03/12 杂记
如何解决PHP无法实现多线程的问题
2015/09/25 PHP
服务器迁移php版本不同可能诱发的问题
2015/12/22 PHP
thinkPHP简单实现多个子查询语句的方法
2016/12/05 PHP
JavaScript While 循环基础教程
2007/04/05 Javascript
jQuery 美元符冲突的解决方法
2010/03/28 Javascript
jQuery结合Json提交数据到Webservice,并接收从Webservice返回的Json数据
2011/02/18 Javascript
getAsDataURL在Firefox7.0下无法预览本地图片的解决方法
2013/11/15 Javascript
JS小功能(button选择颜色)简单实例
2013/11/29 Javascript
jquery解析XML字符串和XML文件的方法说明
2014/02/21 Javascript
批量修改标签css样式以input标签为例
2014/07/31 Javascript
推荐一个封装好的getElementsByClassName方法
2014/12/02 Javascript
关于Javascript加载执行优化的研究报告
2014/12/16 Javascript
2014 年最热门的21款JavaScript框架推荐
2014/12/25 Javascript
jquery获取节点名称
2015/04/26 Javascript
Js+php实现异步拖拽上传文件
2015/06/23 Javascript
AngularJS中如何使用$parse或$eval在运行时对Scope变量赋值
2016/01/25 Javascript
jQuery源码解读之extend()与工具方法、实例方法详解
2017/03/30 jQuery
基于jQuery实现定位导航位置效果
2017/11/15 jQuery
vue检测对象和数组的变化分析
2018/06/30 Javascript
对layui中表单元素的使用详解
2018/08/15 Javascript
JavaScript 接口原理与用法实例详解
2020/05/12 Javascript
[54:47]Liquid vs VP Supermajor决赛 BO 第五场 6.10
2018/07/05 DOTA
Python实现base64编码的图片保存到本地功能示例
2018/06/22 Python
python 同时运行多个程序的实例
2019/01/07 Python
python名片管理系统开发
2020/06/18 Python
Kathmandu英国网站:新西兰户外运动品牌
2017/03/27 全球购物
Crabtree & Evelyn欧盟:豪华洗浴、身体和护发
2021/03/09 全球购物
Servlet的生命周期
2013/08/25 面试题
文科生自我鉴定
2014/02/15 职场文书
2014年小学教导处工作总结
2014/12/19 职场文书
2014年小班保育员工作总结
2014/12/23 职场文书
民主生活会意见
2015/06/05 职场文书
python 实现图片特效处理
2022/04/03 Python
uniapp 微信小程序 自定义tabBar 导航
2022/04/22 Javascript
java中如何截取字符串最后一位
2022/07/07 Java/Android