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 相关文章推荐
BBS(php &amp; mysql)完整版(一)
Oct 09 PHP
如何实现给定日期的若干天以后的日期
Oct 09 PHP
用 PHP5 轻松解析 XML
Dec 04 PHP
安装PHP可能遇到的问题“无法载入mysql扩展” 的解决方法
Apr 16 PHP
PHP数据库调用类调用实例(详细注释)
Jul 12 PHP
利用PHP扩展vld查看PHP opcode操作步骤
Mar 04 PHP
使用php判断网页是否gzip压缩
Jun 25 PHP
php数组保存文本与文本反编成数组实例
Nov 13 PHP
PHP入门教程之使用Mysqli操作数据库的方法(连接,查询,事务回滚等)
Sep 11 PHP
ThinkPHP中调用PHPExcel的实现代码
Apr 08 PHP
PHP实现微信图片上传到服务器的方法示例
Jun 29 PHP
浅谈php调用python文件
Mar 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
Thinkphp和onethink实现微信支付插件
2016/04/13 PHP
基于PHP实现短信验证码接口(容联运通讯)
2016/09/06 PHP
详解php中的implements 使用
2017/06/13 PHP
基于jquery的内容循环滚动小模块(仿新浪微博未登录首页滚动微博显示)
2011/03/28 Javascript
使用jquery自定义鼠标样式满足个性需求
2013/11/05 Javascript
基于jQuery+Cookie实现的防止刷新的在线考试倒计时
2015/06/19 Javascript
使用javaScript动态加载Js文件和Css文件
2015/10/24 Javascript
javascript轻量级库createjs使用Easel实现拖拽效果
2016/02/19 Javascript
Javascript之Number对象介绍
2016/06/07 Javascript
jQuery自定义数值抽奖活动代码
2016/06/11 Javascript
Js 获取、判断浏览器版本信息的简单方法
2016/08/08 Javascript
js阻止冒泡和默认事件(默认行为)详解
2016/10/20 Javascript
js实现功能比较全面的全选和多选
2017/03/02 Javascript
封装运动框架实战左右与上下滑动的焦点轮播图(实例)
2017/10/17 Javascript
在vue里使用codemirror遇到的问题
2018/11/01 Javascript
跟混乱的页面弹窗说再见
2019/04/11 Javascript
微信小程序利用Canvas绘制图片和竖排文字详解
2019/06/25 Javascript
Vue Router 实现动态路由和常见问题及解决方法
2020/03/06 Javascript
用Python从零实现贝叶斯分类器的机器学习的教程
2015/03/31 Python
Python实现将xml导入至excel
2015/11/20 Python
Python logging管理不同级别log打印和存储实例
2018/01/19 Python
python tensorflow学习之识别单张图片的实现的示例
2018/02/09 Python
Python网络编程之TCP套接字简单用法示例
2018/04/09 Python
浅析Python pandas模块输出每行中间省略号问题
2018/07/03 Python
简单了解python 邮件模块的使用方法
2019/07/24 Python
利用Python绘制有趣的万圣节南瓜怪效果
2019/10/31 Python
python批量替换文件名中的共同字符实例
2020/03/05 Python
幼儿园运动会入场词
2014/02/10 职场文书
新农村建设标语
2014/06/24 职场文书
授权委托书范文
2014/07/31 职场文书
护士长2014年度工作总结
2014/11/11 职场文书
作文评语集锦
2014/12/25 职场文书
幼儿园秋季开学通知
2015/07/16 职场文书
给原生html中添加水印遮罩层的实现示例
2021/04/02 Javascript
win10下go mod配置方式
2021/04/25 Golang
python unittest单元测试的步骤分析
2021/08/02 Python