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 已经成熟
Dec 04 PHP
PHP中遍历stdclass object的实现代码
Jun 09 PHP
深入了解PHP类Class的概念
Jun 14 PHP
几道坑人的PHP面试题 试试看看你会不会也中招
Aug 19 PHP
初识laravel5
Mar 02 PHP
php代码检查代理ip的有效性
Aug 19 PHP
php分页查询的简单实现代码
Mar 14 PHP
php7基于递归实现删除空文件夹的方法示例
Jun 15 PHP
PHP单例模式应用示例【多次连接数据库只实例化一次】
Dec 18 PHP
laravel 实现划分admin和home 模块分组
Oct 15 PHP
PHP CURL实现模拟登陆并上传文件操作示例
Jan 02 PHP
PHP文件操作简单介绍及函数汇总
Dec 11 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
浅析Apache中RewriteCond规则参数的详细介绍
2013/06/30 PHP
php生成Android客户端扫描可登录的二维码
2016/05/13 PHP
用Javascript实现UTF8编码转换成gb2312编码
2006/12/22 Javascript
JavaScript 基础知识 被自己遗忘的
2009/10/15 Javascript
Javascript 中介者模式实例
2009/12/16 Javascript
jQuery UI Datepicker length为空或不是对象错误的解决方法
2010/12/19 Javascript
javascript 循环调用示例介绍
2013/11/20 Javascript
JavaScript使用位运算符判断奇数和偶数的方法
2015/06/01 Javascript
微信小程序去哪里找 小程序到底如何使用(附小程序名单)
2017/01/09 Javascript
Bootstrap导航条学习使用(一)
2017/02/08 Javascript
详解vue 计算属性与方法跟侦听器区别(面试考点)
2018/04/23 Javascript
实例分析vue循环列表动态数据的处理方法
2018/09/28 Javascript
mockjs+vue页面直接展示数据的方法
2018/12/19 Javascript
利用node 判断打开的是文件 还是 文件夹的实例
2019/06/10 Javascript
微信小程序实现商城倒计时
2020/11/01 Javascript
微信小程序文章详情页跳转案例详解
2019/07/09 Javascript
layui实现checkbox的目录树tree的例子
2019/09/12 Javascript
vue简单练习 桌面时钟的实现代码实例
2019/09/19 Javascript
vue keep-alive实现多组件嵌套中个别组件存活不销毁的操作
2020/10/30 Javascript
vue实现广告栏上下滚动效果
2020/11/26 Vue.js
python实现调用其他python脚本的方法
2014/10/05 Python
Python脚本实时处理log文件的方法
2016/11/21 Python
python使用正则表达式匹配字符串开头并打印示例
2017/01/11 Python
Python解惑之True和False详解
2017/04/24 Python
Python入门之三角函数atan2()函数详解
2017/11/08 Python
解决python使用open打开文件中文乱码的问题
2017/12/29 Python
Pycharm激活方法及详细教程(详细且实用)
2020/05/12 Python
pytest fixtures装饰器的使用和如何控制用例的执行顺序
2021/01/28 Python
Html5移动端弹幕动画实现示例代码
2018/08/27 HTML / CSS
英国的屈臣氏:Boots博姿
2017/12/23 全球购物
巴西最大的在线约会网站:ParPerfeito
2018/07/11 全球购物
金智子午JAVA面试题
2015/09/04 面试题
银行催款通知书
2015/04/17 职场文书
焦裕禄观后感
2015/06/03 职场文书
2015年国庆节广播稿
2015/08/19 职场文书
MongoDB数据库之添删改查
2022/04/26 MongoDB