首页 php教程 php手册 PHP: chr和pack、unpack那些事

PHP: chr和pack、unpack那些事

Jun 06, 2016 pm 08:01 PM
php

引子 我之前有篇文详细介绍过pack和unpack:PHP: 深入pack/unpack,如果有不明白的地方,建议再回过头去看多几遍。现在应该能够写出以下代码: ?php echo pack( C , 97 ). \n ; $ php-ftest.phpa 但是,为什么会输出'a'呢?虽然我们知道字符'a'的ASCII码就是

引子

我之前有篇文详细介绍过pack和unpack:PHP: 深入pack/unpack,如果有不明白的地方,建议再回过头去看多几遍。现在应该能够写出以下代码:

<span><?php </span>
<span>echo</span> pack(<span>"C"</span>, <span>97</span>) . <span>"\n"</span>;</span>
登录后复制
<span>$ </span>php -f test.php
a
登录后复制

但是,为什么会输出'a'呢?虽然我们知道字符'a'的ASCII码就是97,但是pack方法返回的是二进制字符串,为什么不是输出一段二进制而是'a'?为了确认pack方法返回的是一段二进制字符串,这里我对官方的pack的描述截了个图:

PHP: chr和pack、unpack那些事

确实如此,pack返回包含二进制字符串的数据,接下来详细进行分析。

程序是如何显示字符的

这里所说的'程序',其实是个宏观的概念。

对于在控制台中执行脚本(这里是指PHP作为cli脚本来执行),脚本的输出会写入标准输出(stdin)或标准错误(stderr),当然也有可能会重定向到某个文件描述符。拿标准输出来说,暂且忽略它是行缓冲、全缓冲或者是无缓冲。脚本进程执行完毕后如果有输出则会在控制台上输出字符串。那这里的控制台就是所说的'程序'。

对于Web来说(这里是指PHP作为Web的服务器端语言),程序执行完后会将结果响应给浏览器或其它UserAgent,为了方便描述,这里统一称为UserAgent。这里的UserAgent就是所说的'程序'。

当然还有其它情况,比如在GUI窗口中的输出,编辑器打开一个文件等等,这都涉及到如何显示字符串的问题。

在控制台中执行

控制台通过shell命令来执行脚本,它会fork一个子进程,之后通过exec替换子进程的地址空间,因为这个子进程不是会话首进程,所以它可以关联到终端。脚本输出执行完毕后退出,回到控制台。来看下面的例子:

<span><?php </span>
<span>$str </span>= <span>'回'</span>;
<span>echo</span> <span>$str </span>. <span>"\n"</span>;</span>
登录后复制
<span>$ </span>php -f test.php
回
登录后复制

test.php是UTF-8格式的文件,我的Linux系统的Locales是zh_CN.UTF-8。

<span>$ </span>locale
<span>LANG</span>=zh_CN.<span>UTF</span>-<span>8</span>
<span>LANGUAGE</span>=
<span>LC_CTYPE</span>=<span>"zh_CN.UTF-8"</span>
<span>LC_NUMERIC</span>=<span>"zh_CN.UTF-8"</span>
<span>LC_TIME</span>=<span>"zh_CN.UTF-8"</span>
<span>LC_COLLATE</span>=<span>"zh_CN.UTF-8"</span>
<span>LC_MONETARY</span>=<span>"zh_CN.UTF-8"</span>
<span>LC_MESSAGES</span>=<span>"zh_CN.UTF-8"</span>
<span>LC_PAPER</span>=<span>"zh_CN.UTF-8"</span>
<span>LC_NAME</span>=<span>"zh_CN.UTF-8"</span>
<span>LC_ADDRESS</span>=<span>"zh_CN.UTF-8"</span>
<span>LC_TELEPHONE</span>=<span>"zh_CN.UTF-8"</span>
<span>LC_MEASUREMENT</span>=<span>"zh_CN.UTF-8"</span>
<span>LC_IDENTIFICATION</span>=<span>"zh_CN.UTF-8"</span>
<span>LC_ALL</span>=
登录后复制

回到刚才的代码,test.php是UTF-8编码的文件,汉字'回'是三个字节表示的UTF8字符(如果不明白,可以看我的另一篇文章: JavaScript: 详解Base64编码和解码 ),所以test.php文件的内容保存在硬盘上的数据就是4个字节('\n'是ASCII字符,用1个字节表示)。将test.php输出时,将这4个字节发送到标准输出,之后被冲洗(这里忽略掉被flush的时机),由控制台来显示。回想一下Linux系统上的locale设置,很显然是采用UTF8的机制来显示字符,所以前三个字节被当成一个UTF8字符,它被组合在一起转成Unicode码然后查表,再显示出来。

<span><?php </span>
<span>$str </span>= <span>'回'</span>;
<span>echo</span> <span>$str </span>. <span>"\n"</span>;
<span>echo</span> <span>$str</span>{<span>0</span>} . <span>$str</span>{<span>1</span>} . <span>$str</span>{<span>2</span>} . <span>"\n"</span>;</span>
登录后复制
<span>$ </span>php -f test.php
回
回
登录后复制

可以看到,不管是整个字符输出,还是三个字节连在一起输出,结果是一样的。我们接下来看看不同平台上同一个字符的Unicode编码和UTF-8编码是否一样:

PHP测试: 

<span><?php </span>
<span>$str </span>= <span>'回'</span>;
<span>$bin </span>= pack(<span>"C3"</span>, ord(<span>$str</span>{<span>0</span>}), ord(<span>$str</span>{<span>1</span>}), ord(<span>$str</span>{<span>2</span>}));
<span>$hex </span>= strtoupper(bin2hex(<span>$bin</span>));
<span>echo</span> <span>"UTF-8编码: "</span> . <span>$hex </span>. <span>"\n"</span>;
<span>/**
* 1110xxxx 10xxxxxx 10xxxxxx
*/</span>
<span>$byte1 </span>= ord(<span>$str</span>{<span>0</span>});
<span>$byte2 </span>= ord(<span>$str</span>{<span>1</span>});
<span>$byte3 </span>= ord(<span>$str</span>{<span>2</span>});
<span>$c1    </span>= ((<span>$byte1 </span>& <span>0x0F</span>) 4</span>) | ((<span>$byte2 </span>& <span>0x3F</span>) >> <span>2</span>);
<span>$c2    </span>= ((<span>$byte2 </span>& <span>0x03</span>) 6) | (<span>$byte3 </span>& <span>0x3F</span>);
<span>$dec   </span>= ((<span>$c1 </span>& <span>0x00FF</span>) 8) | <span>$c2</span>;
<span>echo</span> <span>"Unicode编码: "</span> . <span>$dec </span>. <span>"\n"</span>;
登录后复制
<span>$ </span>php -f test.php
<span>UTF</span>-<span>8</span>编码<span>:</span> <span>E59B9E</span>
<span>Unicode</span>编码<span>:</span> <span>22238</span>
登录后复制

JavaScript测试: 

<span>script</span> <span>type</span>=<span>"text/javascript"</span>><span>
<span>/**
* UTF16和UTF8转换对照表
* U+00000000 – U+0000007F 	0xxxxxxx
* U+00000080 – U+000007FF 	110xxxxx 10xxxxxx
* U+00000800 – U+0000FFFF 	1110xxxx 10xxxxxx 10xxxxxx
* U+00010000 – U+001FFFFF 	11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
* U+00200000 – U+03FFFFFF 	111110xx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx
* U+04000000 – U+7FFFFFFF 	1111110x 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx
*/</span>
<span>var</span> code = (<span>'回'</span>).charCodeAt(<span>0</span>);
<span>// 1110xxxx</span>
<span>var</span> byte1 = <span>0xE0</span> | ((code >> <span>12</span>) & <span>0x0F</span>);
<span>// 10xxxxxx</span>
<span>var</span> byte2 = <span>0x80</span> | ((code >> <span>6</span>) & <span>0x3F</span>);
<span>// 10xxxxxx</span>
<span>var</span> byte3 = <span>0x80</span> | (code & <span>0x3F</span>);

console.group(<span>'Test chr: '</span>);
console.log(<span>"UTF-8编码:"</span>, byte1.toString(<span>16</span>).toUpperCase() + <span>''</span> + byte2.toString(<span>16</span>).toUpperCase() + <span>''</span> + byte3.toString(<span>16</span>).toUpperCase());
console.log(<span>"Unicode编码: "</span>, code);
console.groupEnd();
</span><span><span>script</span>></span>
登录后复制

PHP: chr和pack、unpack那些事

我们看到输出是一样的。

作为Web的服务器端语言执行

这次无非是由刚才的控制台执行变成了UserAgent,其实道理还是一样的。服务器端PHP脚本输出会通过HTTP的响应返回给UserAgent,那么UserAgent就要对它进行显示。当然,这里还有点例外。数据是通过网络作为字节流发送回UserAgent,通常UserAgent有几种方式来判断字节流是属于什么编码(或许还涉及到压缩,但这里将不考虑这个因素)。

服务器端可以通过响应头部来告诉UserAgent应该用什么编码来处理这些数据,比如:

<span><?php header("Content-Type: text/html; charset=utf8");</span></span>
登录后复制
<span>meta charset="utf-8" </span>/>
登录后复制

但是万一这两种方式都没有提供,那也只能靠猜了。事实也确实如此,据我所知,Firefox就是这么做的,并且将代码开源了: universalchardet 。但是这种方式并不能百分之百正确检测,所以偶尔会访问到乱码的页面。

编辑器打开一个文件

在windows上用notepad新建文本文件另存为时有几种编码选项:ANSI, Unicode, Unicode BigEndian, UTF-8。

PHP: chr和pack、unpack那些事

在其它编辑器中选项更多,包括有BOM和无BOM的。BOM是文件头的前几个字节,通过BOM,处理它的程序就知道这个文件是采用什么编码,并且是什么字节序。然而在PHP中,从来都没有将BOM考虑进去,所以PHP解释器去执行一个PHP文件时,不会忽略前几个BOM字节,这就导致了问题。一般的问题在于发送cookie前,BOM被输出了。所以现在一般推荐无BOM的文件。

无BOM有时候也是会有问题的,因为这需要处理它的程序去检测它是什么编码。检测的方式一般是扫描文件,然后根据不同编码的规则来判断二进制。这里举一个出现问题的例子。在windows上新建一个文本文件并保存为ANSI编码,然后在文件中输入'联通',如图所示:

PHP: chr和pack、unpack那些事

保存好后关闭test.txt文件,然后再双击打开,如图所示:

PHP: chr和pack、unpack那些事

我们看到显示的是乱码,具体我们可以分析一下产生乱码的原因。用Editplus新建一个ANSI文件,输入'联通',然后切换到十六进制查看方式,如下图所示:

PHP: chr和pack、unpack那些事

对应的十六进制是:C1 AA CD A8,转成二进制后如下:

11000001 10101010 11001101 10101000
登录后复制

接着我们来看下UTF-8的转换表:

U+<span>00000000</span> – U+<span>0000007F</span> 	<span>0</span>xxxxxxx
U+<span>00000080</span> – U+<span>000007F</span>F 	<span>110</span>xxxxx <span>10</span>xxxxxx
U+<span>00000800</span> – U+<span>0000F</span>FFF 	<span>1110</span>xxxx <span>10</span>xxxxxx <span>10</span>xxxxxx
U+<span>00010000</span> – U+<span>001F</span>FFFF 	<span>11110</span>xxx <span>10</span>xxxxxx <span>10</span>xxxxxx <span>10</span>xxxxxx
U+<span>00200000</span> – U+<span>03F</span>FFFFF 	<span>111110</span>xx <span>10</span>xxxxxx <span>10</span>xxxxxx <span>10</span>xxxxxx <span>10</span>xxxxxx
U+<span>04000000</span> – U+<span>7F</span>FFFFFF 	<span>1111110</span>x <span>10</span>xxxxxx <span>10</span>xxxxxx <span>10</span>xxxxxx <span>10</span>xxxxxx <span>10</span>xxxxxx
登录后复制

很显然都被当作了二字节的UTF-8字符,拿GBK的编码去UTF-8的码表里查,您说能查到吗?

总结

现在我们已经知道了不管是什么编码的数据,总是一个字节一个字的存储,并且在存储时会进行相应的编码转换。比如汉字'回'的GBK编码和UTF-8编码的字节数和编码值都不一样,所以在将GBK的文件另存为UTF-8时必然会存在转换,反之也是一样的。而在读取时如果有BOM就按BOM规定的编码来处理,否则要进行编码检测后再处理。

再说pack

之前讲了这么多编码方面的问题,其实就是为了让大家更好的理解接下来要讲的。pack可以将ASCII进行打包然后输出(事实上就是将一个多字节变成多个单字节,之后可以通过unpack转换回来),这个我们已经知道了。但是方式是有很多种,原理是一样的。我们来详细分析。对pack/unpack不太熟悉的还是建议去翻看我之前的一篇文章:PHP: 深入pack/unpack 。因为本人的机器是小端序的,所以本文只考虑小端序。大端序是一样的方式,只不过字节序不一样罢了,可以先判断本机的字节序再处理。

<span><?php </span>
<span>echo</span> pack(<span>"C"</span>, <span>0x61</span>) . <span>"\n"</span>;
<span>echo</span> pack(<span>"S"</span>, <span>0x6161</span>) . <span>"\n"</span>;
<span>echo</span> pack(<span>"L"</span>, <span>0x61616161</span>) . <span>"\n"</span>;
<span>echo</span> pack(<span>"L"</span>, <span>0x9E9BE561</span>) . <span>"\n"</span>;
<span>echo</span> chr(<span>0xE5</span>) . chr(<span>0x9B</span>) . chr(<span>0x9E</span>) . <span>"\n"</span>;
<span>echo</span> pack(<span>"H6"</span>, <span>"E59B9E"</span>) . <span>"\n"</span>;</span>
登录后复制
<span>$ </span>php -f test.php
a
aa
aaaa
a回
回
回
登录后复制

我们一句句的来分析,首先是:

<span>echo</span> pack(<span>"C"</span>, 0x61) . <span>"\n"</span>;
<span>echo</span> pack(<span>"S"</span>, 0x6161) . <span>"\n"</span>;
<span>echo</span> pack(<span>"L"</span>, 0x61616161) . <span>"\n"</span>;
登录后复制

这三句代码很简单,C是无符号字节,S是2个无符号字节,L是4个无符号字节,所以输出也没什么疑问。无论几个字节,都是ASCII码,0x61的二进制的高位为0,所以能正确显示。

<span>echo</span> pack(<span>"L"</span>, 0x9E9BE561) . <span>"\n"</span>;
登录后复制

我们或许还记得汉字'回'的UTF-8编码为:0xE59B9E,L是按主机字节序打包的,而我的机器是小端序,所以0x9E9BE561打包后就变为:0x61E59B9E。0x61是字符'a'的ASCII码,而后面的三个字节程序通过判断0xE5就能知道这是一个三字节的UTF-8字符,因此这三个字节会转成Unicode码去查表,然后显示。

echo <span>chr</span>(<span>0xE5</span>) . <span>chr</span>(<span>0x9B</span>) . <span>chr</span>(<span>0x9E</span>) . <span>"\n"</span>;
登录后复制

chr是返回ASCII码所代码的字符,它其实不仅仅是转换单字节的字符,对于多字节同样适用。它会根据刚才所说的规则将三个UTF-8字节转成Unicode码然后去查表。

<span>echo</span> pack(<span>"H6"</span>, <span>"E59B9E"</span>) . <span>"\n"</span>;
登录后复制

对于H格式字符,它和h的区别就是前者是高四位在前,后者是低四位在前,但它们都是以半字节为单位读取的,并且以十六进制的方式。您应该看到我在用H进行打包时传的是字符串"E59B9E",如果传的是0xE59B9E就不对了,这样的话先会转成十进制15047582,然后在前面加上0x变成十六进制0x15047582。

所谓按半字节读取其实是这样的,比如0x47,先转成十进制71,然后变成十六进制的0x71。按半字节读取必然会丢弃4位,然后要补0。读取了0x7,对H来说,它是高位,那么在低位补0变成0x70。对于h来说,它是低位,那么在高位补0变成0x07。

再说unpack

unpack是pack的逆函数,当然unpack有自己的语法,但这不是重点,因为这些只是表象。

unpack其实只是将多个字节压缩成一个字节。比如0x12和0x34这两个字节如果要组成一个双字节,则可以使用unpack的S格式化字符来实现,代码如下:

<span><?php </span>
<span>$data </span>= unpack(<span>"S"</span>, pack(<span>"H*"</span>, <span>"3412"</span>));
print_r(<span>$data</span>);
<span>echo</span> <span>'0x'</span> . dechex(<span>$data</span>[<span>1</span>]) . <span>"\n"</span>;</span>
登录后复制
<span>$ php </span>-f test.php
<span>Array</span>
(
    [<span>1</span>] => <span>4660</span>
)
<span>0x1234</span>
登录后复制

因为是小端序,所以要写成"3421"。其实还可以用位运算的方式来实现。这个时候就不需要考虑字节序了,因为字节序只是存储时才需要考虑的问题,对于输出来说,是按照我们自然的方式:

<span><?php </span>
<span>print</span> <span>"0x"</span> . dechex((<span>0x12</span> 8</span>) | <span>0x34</span>) . <span>"\n"</span>;
登录后复制
<span>$ </span>php -f test.php
<span>0x1234</span>
登录后复制

再说chr

PHP官方文档上所描述的chr方法的原型参数是一个int型,虽然形参名为ascii,但不要被骗了。如图所示:

PHP: chr和pack、unpack那些事

chr确实是可以接收一个int类型的参数,而不仅仅是一个ASCII码。还记得之前所做的测试吗?通过chr方法将三个UTF-8的字节组合在一起。很显然UTF-8的每个字节都大于127,因为最高位都是1。

不过说起来chr方法还是比较傻的,比如有如下代码:

<span><?php </span>
<span>echo</span> chr(<span>0xE5</span>) . chr(<span>0x9B</span>) . chr(<span>0x9E</span>) . <span>"\n"</span>;
<span>echo</span> chr(<span>0xE59B9E</span>) . <span>"\n"</span>;</span>
登录后复制
<span>$ </span>php -f test.php
回
?
登录后复制

chr方法完全没有考虑将0xE59B9E拆成三个字节来组合,所以最终是乱码。

再说ord

ord接受一个string类型的参数,它用于返回参数的ASCII码。如下图所示:

PHP: chr和pack、unpack那些事

虽然它只返回ASCII码,但它的参数却不限定。比如您可以传递单字节或多字节。举例如下:

<span><?php </span>
<span>echo</span> ord(<span>"a"</span>) . <span>"\n"</span>;
<span>echo</span> ord(<span>"回"</span>) . <span>"\n"</span>;
<span>echo</span> <span>0xE5</span> . <span>"\n"</span>;</span>
登录后复制
<span>$ </span>php -f test.php
<span>97</span>
<span>229</span>
<span>229</span>
登录后复制

传入汉字'回',它会自动截取第一个字节,然后返回它的十进制表示。

实现自己的pack

理解了原理,其实自己去实现也就是那么回事。本文以格式化字符L为例,L是无符号32位整型,它是按主机字节序来打包的。所以我们要先判断机器的字节序。

<span><?php </span>
<span><span>function</span> <span>IsBigEndian</span><span>()</span>
{</span>
<span>$bin </span>= pack(<span>"L"</span>, <span>0x12345678</span>);
<span>$hex </span>= bin2hex(<span>$bin</span>);
<span>if</span> (ord(pack(<span>"H2"</span>, <span>$hex</span>)) === <span>0x78</span>)
{
<span>return</span> <span>FALSE</span>;
}

<span>return</span> <span>TRUE</span>;
}

<span>if</span> (IsBigEndian())
{
<span>echo</span> <span>"大端序"</span>;
}
<span>else</span>
{
<span>echo</span> <span>"小端序"</span>;
}

<span>echo</span> <span>"\n"</span>;</span>
登录后复制
<span>$ </span>php -f test.php
小端序
登录后复制

代码非常简单,因为PHP不能直接操作内存,所以借助于pack来实现。L格式化字符表示主机字节序,如果机器是小端序,则0x12345678通过L打包后会变成4个字节并且字节序是:0x78, 0x56, 0x34, 0x12,如果是大端序则是:0x12, 0x34, 0x56, 0x78。然后通过H2格式化字符获取1个高字节,如果是0x78那就是小端序,否则就是大端序。

接下来是my_pack方法的实现,仅仅实现了L格式化字符,代码如下:

<?php function IsBigEndian()
{
<span>$bin = <span>pack</span>(<span>"L"</span>, <span>0x12345678</span>);
<span>$hex</span> = bin2hex(<span>$bin</span>);
<span>if</span> (<span>ord</span>(<span>pack</span>(<span>"H2"</span>, <span>$hex</span>)) === <span>0x78</span>)
{
<span>return</span> FALSE;
}

<span>return</span> TRUE;
}

function my_pack(<span>$num</span>)
{
<span>$bin</span>     = <span>""</span>;
<span>$padding</span> = <span>0</span>;
<span>if</span> (<span>$num</span> >= <span>0x00</span> && <span>$num</span> 0xFF)
{
<span>//</span> 补<span>3</span>个字节
<span>$padding</span> = str_repeat(<span>chr</span>(<span>0</span>), <span>3</span>);
<span>if</span> (!IsBigEndian())
{
<span>$bin</span> = <span>chr</span>(<span>$num</span>) . <span>$padding</span>;
}
<span>else</span>
{
<span>$bin</span> = <span>$padding</span> . <span>chr</span>(<span>$num</span>);
}
}
<span>else</span> <span>if</span> (<span>$num</span> > <span>0xFF</span> && <span>$num</span> 0xFFFF)
{
<span>//</span> 补<span>2</span>个字节
<span>$padding</span> = str_repeat(<span>chr</span>(<span>0</span>), <span>2</span>);
<span>$byte3</span>   = (<span>$num</span> >> <span>8</span>) & <span>0xFF</span>;
<span>$byte4</span>   = <span>$num</span> & <span>0xFF</span>;
<span>//</span> 如果是小端序,则按小端序方式
<span>if</span> (!IsBigEndian())
{
<span>$bin</span> = <span>chr</span>(<span>$byte4</span>) . <span>chr</span>(<span>$byte3</span>) . <span>$padding</span>;
}
<span>else</span>
{
<span>$bin</span> = <span>$padding</span> . <span>chr</span>(<span>$byte3</span>) . <span>chr</span>(<span>$byte4</span>);
}
}
<span>else</span> <span>if</span> (<span>$num</span> > <span>0xFFFF</span> && <span>$num</span> 0x7FFFFF)
{
<span>//</span> 补<span>1</span>个字节
<span>$padding</span> = <span>chr</span>(<span>0</span>);
<span>$byte2</span>   = (<span>$num</span> >> <span>16</span>) & <span>0xFF</span>;
<span>$byte3</span>   = (<span>$num</span> >> <span>8</span>) & <span>0xFF</span>;
<span>$byte4</span>   = <span>$num</span> & <span>0xFF</span>;
<span>//</span> 如果是小端序,则按小端序方式
<span>if</span> (!IsBigEndian())
{
<span>$bin</span> = <span>chr</span>(<span>$byte4</span>) . <span>chr</span>(<span>$byte3</span>) . <span>chr</span>(<span>$byte2</span>) . <span>$padding</span>;
}
<span>else</span>
{
<span>$bin</span> = <span>$padding</span> . <span>chr</span>(<span>$byte2</span>) . <span>chr</span>(<span>$byte3</span>) . <span>chr</span>(<span>$byte4</span>);
}
}
<span>else</span>
{
<span>$byte1</span> = (<span>$num</span> >> <span>24</span>) & <span>0xFF</span>;
<span>$byte2</span> = (<span>$num</span> >> <span>16</span>) & <span>0xFF</span>;
<span>$byte3</span> = (<span>$num</span> >> <span>8</span>) & <span>0xFF</span>;
<span>$byte4</span> = <span>$num</span> & <span>0xFF</span>;
<span>//</span> 如果是小端序,则按小端序方式
<span>if</span> (!IsBigEndian())
{
<span>$bin</span> = <span>chr</span>(<span>$byte4</span>) . <span>chr</span>(<span>$byte3</span>) . <span>chr</span>(<span>$byte2</span>) . <span>chr</span>(<span>$byte1</span>);
}
<span>else</span>
{
<span>$bin</span> = <span>chr</span>(<span>$byte1</span>) . <span>chr</span>(<span>$byte2</span>) . <span>chr</span>(<span>$byte3</span>) . <span>chr</span>(<span>$byte4</span>);
}
}

<span>return</span> <span>$bin</span>;
}

<span>$bin</span> = my_pack(<span>0x12</span>);
print_r(<span>unpack</span>(<span>"L"</span>, <span>$bin</span>));
<span>$bin</span> = <span>pack</span>(<span>"L"</span>, <span>0x12</span>);
print_r(<span>unpack</span>(<span>"L"</span>, <span>$bin</span>));

<span>$bin</span> = my_pack(<span>0x1234</span>);
print_r(<span>unpack</span>(<span>"L"</span>, <span>$bin</span>));
<span>$bin</span> = <span>pack</span>(<span>"L"</span>, <span>0x1234</span>);
print_r(<span>unpack</span>(<span>"L"</span>, <span>$bin</span>));

<span>$bin</span> = my_pack(<span>0x123456</span>);
print_r(<span>unpack</span>(<span>"L"</span>, <span>$bin</span>));
<span>$bin</span> = <span>pack</span>(<span>"L"</span>, <span>0x123456</span>);
print_r(<span>unpack</span>(<span>"L"</span>, <span>$bin</span>));

<span>$bin</span> = my_pack(<span>0x12345678</span>);
print_r(<span>unpack</span>(<span>"L"</span>, <span>$bin</span>));
<span>$bin</span> = <span>pack</span>(<span>"L"</span>, <span>0x12345678</span>);
print_r(<span>unpack</span>(<span>"L"</span>, <span>$bin</span>));
登录后复制
<span>$ php </span>-f test.php
<span>Array</span>
(
    [<span>1</span>] => <span>18</span>
)
<span>Array</span>
(
    [<span>1</span>] => <span>18</span>
)
<span>Array</span>
(
    [<span>1</span>] => <span>4660</span>
)
<span>Array</span>
(
    [<span>1</span>] => <span>4660</span>
)
<span>Array</span>
(
    [<span>1</span>] => <span>1193046</span>
)
<span>Array</span>
(
    [<span>1</span>] => <span>1193046</span>
)
<span>Array</span>
(
    [<span>1</span>] => <span>305419896</span>
)
<span>Array</span>
(
    [<span>1</span>] => <span>305419896</span>
)
登录后复制

测试中调用pack和my_pack的结果是一样的。unpack的实现就是pack的逆操作,只需把pack的结果的每一个字节取到它的ASCII码(可以通过ord方法来做),然后将4个字节根据高低位次序(这还要根据大小端)通过位运算变成一个4字节的整数,其它格式化字符也是类似如此实现。

关于ISO 8859-1编码

ISO 8859-1又称  Latin-1  或西欧语言。是国际标准化组织内ISO/IEC 8859的第一个8位字符集。它以ASCII为基础,在空置的0xA0-0xFF的范围内,加入96个字母及符号,藉以供使用附加符号的拉丁字母语言使用。

从定义可知,Latin-1编码是单字节编码,向下兼容  ASCII  ,其编码范围是0x00~0xFF。0x00~0x7F之间完全和ASCII码一致,0x80~0x9F之间是控制字符,0xA0~0xFF之间是文字符号。

ISO-8859-1收录的字符除ASCII收录的字符外,还包括西欧语言、希腊语、泰语、阿拉伯语、希伯来语对应的文字符号。欧元符号出现的比较晚,没有被收录在ISO-8859-1当中。

因为ISO-8859-1编码范围使用了单字节内的所有空间,在支持ISO-8859-1的系统中传输和存储其他任何编码的字节流都不会被抛弃。换言之,把其他任何编码的字节流当作ISO-8859-1编码看待都没有问题。这是个很重要的特性,MySQL数据库默认编码是Latin-1就是利用了这个特性。ASCII编码是一个7位的容器,ISO-8859-1编码是一个8位的容器。

结束语

pack/unpack在实际工作中用得非常多,因为很多公司用PHP做前端,通过TCP调用接口,这就需要用到pack/unpack来打包和解包。希望本文能对大家有帮助。

本站声明
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn

热AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智能驱动的应用程序,用于创建逼真的裸体照片

AI Clothes Remover

AI Clothes Remover

用于从照片中去除衣服的在线人工智能工具。

Undress AI Tool

Undress AI Tool

免费脱衣服图片

Clothoff.io

Clothoff.io

AI脱衣机

Video Face Swap

Video Face Swap

使用我们完全免费的人工智能换脸工具轻松在任何视频中换脸!

热门文章

<🎜>:泡泡胶模拟器无穷大 - 如何获取和使用皇家钥匙
3 周前 By 尊渡假赌尊渡假赌尊渡假赌
北端:融合系统,解释
3 周前 By 尊渡假赌尊渡假赌尊渡假赌
Mandragora:巫婆树的耳语 - 如何解锁抓钩
3 周前 By 尊渡假赌尊渡假赌尊渡假赌

热工具

记事本++7.3.1

记事本++7.3.1

好用且免费的代码编辑器

SublimeText3汉化版

SublimeText3汉化版

中文版,非常好用

禅工作室 13.0.1

禅工作室 13.0.1

功能强大的PHP集成开发环境

Dreamweaver CS6

Dreamweaver CS6

视觉化网页开发工具

SublimeText3 Mac版

SublimeText3 Mac版

神级代码编辑软件(SublimeText3)

热门话题

Java教程
1664
14
CakePHP 教程
1423
52
Laravel 教程
1321
25
PHP教程
1269
29
C# 教程
1249
24
PHP和Python:比较两种流行的编程语言 PHP和Python:比较两种流行的编程语言 Apr 14, 2025 am 12:13 AM

PHP和Python各有优势,选择依据项目需求。1.PHP适合web开发,尤其快速开发和维护网站。2.Python适用于数据科学、机器学习和人工智能,语法简洁,适合初学者。

PHP行动:现实世界中的示例和应用程序 PHP行动:现实世界中的示例和应用程序 Apr 14, 2025 am 12:19 AM

PHP在电子商务、内容管理系统和API开发中广泛应用。1)电子商务:用于购物车功能和支付处理。2)内容管理系统:用于动态内容生成和用户管理。3)API开发:用于RESTfulAPI开发和API安全性。通过性能优化和最佳实践,PHP应用的效率和可维护性得以提升。

PHP:网络开发的关键语言 PHP:网络开发的关键语言 Apr 13, 2025 am 12:08 AM

PHP是一种广泛应用于服务器端的脚本语言,特别适合web开发。1.PHP可以嵌入HTML,处理HTTP请求和响应,支持多种数据库。2.PHP用于生成动态网页内容,处理表单数据,访问数据库等,具有强大的社区支持和开源资源。3.PHP是解释型语言,执行过程包括词法分析、语法分析、编译和执行。4.PHP可以与MySQL结合用于用户注册系统等高级应用。5.调试PHP时,可使用error_reporting()和var_dump()等函数。6.优化PHP代码可通过缓存机制、优化数据库查询和使用内置函数。7

PHP的持久相关性:它还活着吗? PHP的持久相关性:它还活着吗? Apr 14, 2025 am 12:12 AM

PHP仍然具有活力,其在现代编程领域中依然占据重要地位。1)PHP的简单易学和强大社区支持使其在Web开发中广泛应用;2)其灵活性和稳定性使其在处理Web表单、数据库操作和文件处理等方面表现出色;3)PHP不断进化和优化,适用于初学者和经验丰富的开发者。

PHP与Python:了解差异 PHP与Python:了解差异 Apr 11, 2025 am 12:15 AM

PHP和Python各有优势,选择应基于项目需求。1.PHP适合web开发,语法简单,执行效率高。2.Python适用于数据科学和机器学习,语法简洁,库丰富。

PHP和Python:代码示例和比较 PHP和Python:代码示例和比较 Apr 15, 2025 am 12:07 AM

PHP和Python各有优劣,选择取决于项目需求和个人偏好。1.PHP适合快速开发和维护大型Web应用。2.Python在数据科学和机器学习领域占据主导地位。

PHP与其他语言:比较 PHP与其他语言:比较 Apr 13, 2025 am 12:19 AM

PHP适合web开发,特别是在快速开发和处理动态内容方面表现出色,但不擅长数据科学和企业级应用。与Python相比,PHP在web开发中更具优势,但在数据科学领域不如Python;与Java相比,PHP在企业级应用中表现较差,但在web开发中更灵活;与JavaScript相比,PHP在后端开发中更简洁,但在前端开发中不如JavaScript。

PHP和Python:解释了不同的范例 PHP和Python:解释了不同的范例 Apr 18, 2025 am 12:26 AM

PHP主要是过程式编程,但也支持面向对象编程(OOP);Python支持多种范式,包括OOP、函数式和过程式编程。PHP适合web开发,Python适用于多种应用,如数据分析和机器学习。

See all articles