PHP中的pack函数和unpack函数的详细介绍
发布时间:2022-08-05 09:47:41 所属栏目:PHP教程 来源:互联网
导读:本篇文章给大家带来的内容是关于PHP中的pack函数和unpack函数的详细介绍(附代码),有一定的参考价值,有需要的朋友可以参考一下,希望对你有所帮助。 PHP有两个重要的冷门函数:pack和unpack。在网络编程,读写图像文件等场景,这两个函数几乎必不可少。鉴
|
本篇文章给大家带来的内容是关于PHP中的pack函数和unpack函数的详细介绍(附代码),有一定的参考价值,有需要的朋友可以参考一下,希望对你有所帮助。 PHP有两个重要的冷门函数:pack和unpack。在网络编程,读写图像文件等场景,这两个函数几乎必不可少。鉴于文件读写/网络编程,或者说字节流处理的重要性,掌握这两个函数是迈向高级PHP编程的基础。 本文先介绍字节和字符的区别,说明两个函数存在的必要性和重要性。然后介绍基本用法和使用场景,让读者对其有大体了解,为实际使用中奠定基础。 字节和字符: PHP的优势是简单易用,熟练运用 字符串 和 数组 相关函数就能抗住一般的需求。日常工作中多用到字符串,所以PHP开发对字符都比较熟悉,稍微资深点基本能也能弄清字符编码。但字符的伴生概念:字节,不少PHP开发并不知晓/熟悉。 这不怪他们。PHP世界里极少出现“字节(流)”的概念:没有byte关键字(当然也没有char),官方文档也没提字节;没有原生的数组支持(常用的array其实是hashtable);当然字符串(string)能表达其他语言中的字节数组(Byte Array, byte[])。 字节和字符有什么联系和区别呢?简单来说字节是计算机存储和操作的最小单位,字符是人们阅读的最小单位;字节是存储(物理)概念,字符是逻辑概念;字节代表数据(内涵和本质),字符代表其含义;字符由字节组成。 举几个例子说明两者区别:“中国”包含2个字符,GBK编码表示需要4个字节,UTF-8编码需要6个字节;数字“1234567890”,包含10个字符,用int32类型表示只需4个字节;下面的图片占用42582个字节,用字符表示是“我老婆”,只占用3个字符: 再举一个常用的例子说明字符和字节的区别。开发中我们常用md5算法获取数据的哈希值,算法返回一个128位(bit)的数据(16个字节)。为方便查看其值,人们约定成俗地用十六进制表示,结果就是我们熟知的32位长度的字符串(不区分大小写)。32长度字符串不是md5算法的必然结果,16字节数据才是其本质。如果你愿意,可以用一个小于2^128的数字表示哈希结果,也可以将16字节base64编码后作为其结果。所以常用的32位哈希值与md5返回的16字节关系为:一个是字符表示,另一个则是其本质(字符数组)(PHP的md5函数第二个参数值为true便可得到16字节数据,或hash函数第三个参数为true)。 相关概念还有字节序、字符编码等,本文不做展开。 引言: PHP中专门处理字符串的函数有几十个,加上正则、时间等函数,字符串处理的函数不下百个。相比之下字节处理门庭冷落,相关函数寥寥无几。除了常用的ord/chr,哈希加密函数返回的原始字节、openssl库的openssl_random_pseudo_bytes等函数真正处理或返回 字节外,最重要的两个字节处理函数是pack和unpack。 本节从问题引出pack函数的使用。 问题: 考虑一个简单的问题:宇宙的终极答案42在内存中是如何表示的(或者说怎么获取其字节数组)? 因为42是一个整数,根据硬件不同,其占用字节大小可能为1, 2, 4, 8等。这里我们限定一个整数占用4个字节,于是问题的等价表述为:怎样将一个整数转换成字节数组(本机序,4个字节)? 分析: 因为是多字节,所以要考虑字节序的问题。42不超过255,只占用一个字节,故而其他三个字节都是0。据此得到结论:如果是大端序(低位字节存放在地址高位),四个字节分别是:0 0 0 42;如果是小端序,结果则是:42 0 0 0。 那怎么知道机器的字节序呢?PHP没有提供相关功能,也不能像C语言直接取地址访问字节数据。无所不能的PHP该怎么搞定字节序,或者说完成数据向字节的转换? 方案: PHP应用层面,数据向字节(数组)的转换是pack的专场,字节(数组)向数据的转换则是unpack的专场。除这两个函数,字节数组(或二进制数据)向数据的转换几无可能(如果有请不吝指教)。 现在我们用pack函数获取42在内存中的字节数组。相关代码如下: function intToBytes(int $num) : string { return pack("l", $num); } function outputBytes(string $bytes) { echo "bytes: "; for ($i = 0; $i < strlen($bytes); ++ $i) { echo ord($bytes[$i]), " "; } echo PHP_EOL; } //phpfensi.com outputBytes(intToBytes(42)); // 程序输出: bytes: 42 0 0 0 本人计算机用的英特尔的CPU,x86架构是小端序,所以程序输出符合预期。 延伸一下,怎么判断机器的字节序?有了pack函数,答案非常简单: function bigEndian() : bool { $data = 0x1200; $bytes = pack("s", $data); return ord($bytes[0]) === 0x12; } 调用函数便返回本机是否大端序。 上述是pack函数简单的使用场景,接下来分别介绍pack和unpack函数。 pack和unpack pack函数 pack是“打包/封包”的意思。如其名,pack函数的工作是将数据按照格式打包成字节数组。函数原型为: pack ( string $format [, mixed $... ] ) : string 形式上与printf系列函数相同:第一个参数是格式字符串,其余参数是要格式化的参数。不同之处在于pack函数的格式中不能出现元字符和量词外的其他字符,所以不需要%符号。 上文的例子中使用了"l"和"s"两个格式化元字符,pack函数的元字符主要分为三类: 字符串:a、A等;将数据转成字符串,功能上与sprintf类似,例如整数32转换成字符串"32"; 字节:h和H;对字节进行16进制编码,区别在于低位还是高位在前,功能上与dechex等函数类似; char/short/int/long/float/double六种基本类型:c/s/i/l等;将数据转换成对应类型的字节数组,除char类型外(暂)没有其他函数可替代; 注意:char和a/A等的区别是a/A等输入为字符(串),而's/S'的输入要求是小于256的整数,输入字符会得到0。 量词比较简单:数字和""两种。例如"i2"表示将两个参数按照整数转换,"c"表示后续都按照char类型转换。 unpack unpack是pack的反向操作:将字节数组解析成有意义的数据。其函数原型为: unpack ( string $format , string $data [, int $offset = 0 ] ) : array unpack函数需要注意的是第一个参数和返回值。返回值好理解,pack函数相当于将除格式化参数外的参数数组(想象成call_user_func_array的参数)变成一个字节数组;unpack做相反的事情:释放数据,得到输入时的参数数组。 返回一个数组,其键分别是什么呢?这便是格式化参数($format)在pack和unpack的不同之处:unpack应该对释放出来的数据命名,用"/"分隔各组数据。由于格式化参数允许有非元字符和量词外的字符,为了区分数据,不同数据间的"/"分隔符必不可少。 (编辑:滨州站长网) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |

