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 相关文章推荐
Zend Studio for Eclipse的java.lang.NullPointerException错误的解决方法
Dec 06 PHP
php Smarty date_format [格式化时间日期]
Mar 15 PHP
组合算法的PHP解答方法
Feb 04 PHP
PHP删除HTMl标签的实现代码
Jun 30 PHP
php配合jquery实现增删操作具体实例
Dec 12 PHP
Windows下的PHP安装pear教程
Oct 24 PHP
Windows下编译PHP5.4和xdebug全记录
Apr 03 PHP
微信API接口大全
Apr 15 PHP
PHP的Yii框架中View视图的使用进阶
Mar 29 PHP
php array_walk 对数组中的每个元素应用用户自定义函数详解
Nov 18 PHP
老生常谈文本文件和二进制文件的区别
Feb 27 PHP
PHP异常处理定义与使用方法分析
Jul 25 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
在线增减.htpasswd内的用户
2006/10/09 PHP
ThinkPHP中的三大自动简介
2014/08/22 PHP
PHP模拟asp.net的StringBuilder类实现方法
2015/08/08 PHP
php pdo连接数据库操作示例
2019/11/18 PHP
js获取控件位置以及不同浏览器中的差别介绍
2013/08/08 Javascript
js 去除字符串第一位逗号的方法
2014/06/07 Javascript
jQuery原理系列-常用Dom操作详解
2016/06/07 Javascript
AngularJS Toaster使用详解
2017/02/24 Javascript
详解微信小程序 template添加绑定事件
2017/06/23 Javascript
详解vue.js下引入百度地图jsApi的两种方法
2018/07/27 Javascript
js中对象与对象创建方法的各种方法
2019/02/27 Javascript
php结合js实现多条件组合查询
2019/05/28 Javascript
vue中jsonp插件的使用方法示例
2020/09/10 Javascript
[05:41]2014DOTA2西雅图国际邀请赛 小组赛7月10日TOPPLAY
2014/07/10 DOTA
[40:17]2018DOTA2亚洲邀请赛 4.5 淘汰赛 LGD vs Liquid 第一场
2018/04/06 DOTA
Python中操作文件之write()方法的使用教程
2015/05/25 Python
Python中Class类用法实例分析
2015/11/12 Python
在Django中进行用户注册和邮箱验证的方法
2016/05/09 Python
python+matplotlib绘制旋转椭圆实例代码
2018/01/12 Python
Python实现屏幕截图的两种方式
2018/02/05 Python
Python多进程编程multiprocessing代码实例
2020/03/12 Python
浅谈Python中threading join和setDaemon用法及区别说明
2020/05/02 Python
python实现移动木板小游戏
2020/10/09 Python
Python中免验证跳转到内容页的实例代码
2020/10/23 Python
Contém1g官网:巴西彩妆品牌
2020/01/17 全球购物
初中生期末考试的自我评价
2013/12/17 职场文书
开学季活动策划方案
2014/02/28 职场文书
干部考核评语
2014/04/29 职场文书
教师节宣传方案
2014/05/23 职场文书
户籍证明模板
2014/09/28 职场文书
小学教师师德整改措施
2014/09/29 职场文书
写给领导的感谢信
2015/01/22 职场文书
浅析CSS在DevTools 中架构演变
2021/10/05 HTML / CSS
DE1107机评
2022/04/05 无线电
使用Django框架创建项目
2022/06/10 Python
V Rising 服务器搭建图文教程
2022/06/16 Servers