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 相关文章推荐
一个ubbcode的函数,速度很快.
Oct 09 PHP
PHP一些有意思的小区别
Dec 06 PHP
PHP的一个基础知识 表单提交
Jul 04 PHP
基于php常用正则表达式的整理汇总
Jun 08 PHP
php中使用sftp教程
Mar 30 PHP
PHP实现在线阅读PDF文件的方法
Jun 23 PHP
PHP 将数组打乱 shuffle函数的用法及简单实例
Jun 17 PHP
PHP获取真实客户端的真实IP
Mar 07 PHP
PHP 二维array转换json的实例讲解
Aug 21 PHP
PHP实现获取ip地址的5种方法,以及插入用户登录日志操作示例
Feb 28 PHP
Laravel 实现添加多语言提示信息
Oct 25 PHP
PHP设计模式之组合模式定义与应用示例
Feb 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
一些关于PHP的知识
2006/11/17 PHP
超级简单的php+mysql留言本源码
2009/11/11 PHP
使用PHPMYADMIN操作mysql数据库添加新用户和数据库的方法
2010/04/02 PHP
php绘制一个矩形的方法
2015/01/24 PHP
PHP使用文件锁解决高并发问题示例
2018/03/29 PHP
解决Laravel5.5下的toArray问题
2019/10/15 PHP
PHP实现限制域名访问的实现代码(本地验证)
2020/09/13 PHP
js实现运动logo图片效果及运动元素对象sportBox使用方法
2012/12/25 Javascript
JS复制到剪贴板示例代码
2013/10/30 Javascript
ExtJS实现文件下载的方法实例
2013/11/09 Javascript
浅析JavaScript中的typeof运算符
2013/11/30 Javascript
一个简单的Node.js异步操作管理器分享
2014/04/29 Javascript
深入理解javascript构造函数和原型对象
2014/09/23 Javascript
js获取浏览器基本信息大全
2014/11/27 Javascript
jQuery中removeData()方法用法实例
2014/12/27 Javascript
bootstrap实现弹窗和拖动效果
2016/01/03 Javascript
关于vue.js过渡css类名的理解(推荐)
2017/04/10 Javascript
封装运动框架实战左右与上下滑动的焦点轮播图(实例)
2017/10/17 Javascript
对mac下nodejs 更新到最新版本的最新方法(推荐)
2018/05/17 NodeJs
Vue中插入HTML代码的方法
2018/09/21 Javascript
微信小程序如何实现点击图片放大功能
2020/01/21 Javascript
Python的Twisted框架中使用Deferred对象来管理回调函数
2016/05/25 Python
python主线程与子线程的结束顺序实例解析
2019/12/17 Python
关于keras.layers.Conv1D的kernel_size参数使用介绍
2020/05/22 Python
Django日志及中间件模块应用案例
2020/09/10 Python
肯尼亚网上商城:Kilimall
2016/08/20 全球购物
alice McCALL官网:澳大利亚时尚品牌
2020/11/16 全球购物
介绍一下linux的文件权限
2012/02/15 面试题
企业形象策划方案
2014/05/29 职场文书
付款承诺函范文
2015/01/21 职场文书
出生证明范本
2015/06/15 职场文书
煤矿安全生产工作总结
2015/08/13 职场文书
2016党员党章学习心得体会
2016/01/14 职场文书
Python基础之hashlib模块详解
2021/05/06 Python
Python编程中内置的NotImplemented类型的用法
2022/03/23 Python
错误码NET::ERR_CERT_DATE_INVALID证书已过期解决方法?
2022/07/07 数码科技