unpack

(PHP 4, PHP 5, PHP 7, PHP 8)

unpack从二进制字符串解包数据

描述

unpack(string $format, string $string, int $offset = 0): array|false

根据给定的 format 从二进制字符串解包到数组中。

解包的数据存储在关联数组中。要实现此目的,您必须命名不同的格式代码,并用斜杠 / 分隔它们。如果存在重复器参数,则每个数组键将在给定名称后带有序列号。

已经进行了更改,使此函数与 Perl 一致

  • "a" 代码现在保留尾部的 NULL 字节。
  • "A" 代码现在去除所有尾部的 ASCII 空格(空格、制表符、换行符、回车符和 NULL 字节)。
  • 添加了 "Z" 代码用于 NULL 填充字符串,并去除尾部的 NULL 字节。

参数

format

有关格式代码的解释,请参见 pack()

string

已打包的数据。

offset

开始解包的偏移量。

返回值

返回包含二进制字符串解包元素的关联数组,或在失败时返回 false

变更日志

版本 描述
7.2.0 floatdouble 类型支持大端和小端。
7.1.0 添加了可选的 offset

示例

示例 #1 unpack() 示例

<?php
$binarydata
= "\x04\x00\xa0\x00";
$array = unpack("cchars/nint", $binarydata);
print_r($array);
?>

上面的示例将输出

Array
(
    [chars] => 4
    [int] => 160
)

示例 #2 使用重复器的 unpack() 示例

<?php
$binarydata
= "\x04\x00\xa0\x00";
$array = unpack("c2chars/nint", $binarydata);
print_r($array);
?>

上面的示例将输出

Array
(
    [chars1] => 4
    [chars2] => 0
    [int] => 40960
)

注释

警告

请注意,PHP 在内部将整数值存储为有符号数。如果您解包一个大的无符号长整型,并且它的大小与 PHP 在内部存储的值的大小相同,则结果将是一个负数,即使指定了无符号解包。

警告

如果您没有命名元素,则使用从 1 开始的数字索引。请注意,如果您有多个未命名的元素,则某些数据会被覆盖,因为每个元素的编号都从 1 重新开始。

示例 #3 使用未命名键的 unpack() 示例

<?php
$binarydata
= "\x32\x42\x00\xa0";
$array = unpack("c2/n", $binarydata);
var_dump($array);
?>

上面的示例将输出

array(2) {
  [1]=>
  int(160)
  [2]=>
  int(66)
}

请注意,来自 c 说明符的第一个值被来自 n 说明符的第一个值覆盖。

参见

  • pack() - 将数据打包成二进制字符串

添加注释

用户贡献的注释 14 个注释

stanislav dot eckert at vizson dot de
8 年前
一个帮助类,用于将整数转换为二进制字符串,反之亦然。这对于写入和读取文件或套接字中的整数很有用。

<?php

class int_helper
{
public static function
int8($i) {
return
is_int($i) ? pack("c", $i) : unpack("c", $i)[1];
}

public static function
uInt8($i) {
return
is_int($i) ? pack("C", $i) : unpack("C", $i)[1];
}

public static function
int16($i) {
return
is_int($i) ? pack("s", $i) : unpack("s", $i)[1];
}

public static function
uInt16($i, $endianness=false) {
$f = is_int($i) ? "pack" : "unpack";

if (
$endianness === true) { // 大端序
$i = $f("n", $i);
}
else if (
$endianness === false) { // 小端序
$i = $f("v", $i);
}
else if (
$endianness === null) { // 机器字节序
$i = $f("S", $i);
}

return
is_array($i) ? $i[1] : $i;
}

public static function
int32($i) {
return
is_int($i) ? pack("l", $i) : unpack("l", $i)[1];
}

public static function
uInt32($i, $endianness=false) {
$f = is_int($i) ? "pack" : "unpack";

if (
$endianness === true) { // 大端序
$i = $f("N", $i);
}
else if (
$endianness === false) { // 小端序
$i = $f("V", $i);
}
else if (
$endianness === null) { // 机器字节序
$i = $f("L", $i);
}

return
is_array($i) ? $i[1] : $i;
}

public static function
int64($i) {
return
is_int($i) ? pack("q", $i) : unpack("q", $i)[1];
}

public static function
uInt64($i, $endianness=false) {
$f = is_int($i) ? "pack" : "unpack";

if (
$endianness === true) { // 大端序
$i = $f("J", $i);
}
else if (
$endianness === false) { // 小端序
$i = $f("P", $i);
}
else if (
$endianness === null) { // 机器字节序
$i = $f("Q", $i);
}

return
is_array($i) ? $i[1] : $i;
}
}
?>

使用示例
<?php
Header
("Content-Type: text/plain");
include(
"int_helper.php");

echo
int_helper::uInt8(0x6b) . PHP_EOL; // k
echo int_helper::uInt8(107) . PHP_EOL; // k
echo int_helper::uInt8("\x6b") . PHP_EOL . PHP_EOL; // 107

echo int_helper::uInt16(4101) . PHP_EOL; // \x05\x10
echo int_helper::uInt16("\x05\x10") . PHP_EOL; // 4101
echo int_helper::uInt16("\x05\x10", true) . PHP_EOL . PHP_EOL; // 1296

echo int_helper::uInt32(2147483647) . PHP_EOL; // \xff\xff\xff\x7f
echo int_helper::uInt32("\xff\xff\xff\x7f") . PHP_EOL . PHP_EOL; // 2147483647

// 注意:使用 64 位版本的 PHP 测试
echo int_helper::uInt64(9223372036854775807) . PHP_EOL; // \xff\xff\xff\xff\xff\xff\xff\x7f
echo int_helper::uInt64("\xff\xff\xff\xff\xff\xff\xff\x7f") . PHP_EOL . PHP_EOL; // 9223372036854775807

?>
Sergio Santana: ssantana at tlaloc dot imta dot mx
20 年前
这是关于我之前帖子中的最后一个例子。为了清楚起见,我在这里再次包含这个例子,它扩展了正式文档中给出的例子

<?
$binarydata = "AA\0A";
$array = unpack("c2chars/nint", $binarydata);
foreach ($array as $key => $value)
echo "\$array[$key] = $value <br>\n";
?>

这将输出

$array[chars1] = 65
$array[chars2] = 65
$array[int] = 65

这里,我们假设字符 'A' 的 ASCII 码为十进制 65。

记住格式字符串的结构是
<格式代码> [<计数>] [<数组键>] [/ ...],
在这个例子中,格式字符串指示函数执行以下操作
1. ("c2...") 从第二个参数 ("AA ...") 读取两个字符,
2. (...chars...) 使用数组键 "chars1" 和 "chars2" 用于
这两个读取的字符,
3. (.../n...) 从第二个参数 (...\0A") 读取一个短整型,
4. (...int") 使用单词 "int" 作为刚读取的
短整型的数组键。

我希望现在更清楚了,

Sergio。
jjfoerch at earthlink dot net
19 年前
我遇到过这样的情况:我需要在大小端机器上解包一个包含小端序双精度浮点数的文件。PHP 没有格式代码可以更改双精度浮点数的字节序,所以我写了这个解决方法。

<?php
/*以下代码是针对 php 的 unpack 函数的解决方法,
该函数无法解包以与当前机器相反的字节序打包的双精度浮点数。
*/
function big_endian_unpack ($format, $data) {
$ar = unpack ($format, $data);
$vals = array_values ($ar);
$f = explode ('/', $format);
$i = 0;
foreach (
$f as $f_k => $f_v) {
$repeater = intval (substr ($f_v, 1));
if (
$repeater == 0) $repeater = 1;
if (
$f_v{1} == '*')
{
$repeater = count ($ar) - $i;
}
if (
$f_v{0} != 'd') { $i += $repeater; continue; }
$j = $i + $repeater;
for (
$a = $i; $a < $j; ++$a)
{
$p = pack ('d',$vals[$i]);
$p = strrev ($p);
list (
$vals[$i]) = array_values (unpack ('d1d', $p));
++
$i;
}
}
$a = 0;
foreach (
$ar as $ar_k => $ar_v) {
$ar[$ar_k] = $vals[$a];
++
$a;
}
return
$ar;
}

list (
$endiantest) = array_values (unpack ('L1L', pack ('V',1)));
if (
$endiantest != 1) define ('BIG_ENDIAN_MACHINE',1);
if (
defined ('BIG_ENDIAN_MACHINE')) $unpack_workaround = 'big_endian_unpack';
else
$unpack_workaround = 'unpack';
?>

这个解决方法的使用方式如下

<?php

function foo() {
global
$unpack_workaround;
$bar = $unpack_workaround('N7N/V2V/d8d',$my_data);
//...
}

?>

在小端机器上,$unpack_workaround 只是指向 unpack 函数。在大端机器上,它会调用解决方法函数。

注意,此解决方案仅适用于双精度浮点数。在我的项目中,我没有需要检查单精度浮点数的地方。
kennwhite dot nospam at hotmail dot com
19 年前
如果使用/需要以 0 为基的索引,那么可以使用

$int_list = unpack("s*", $some_binary_data);

尝试

$int_list = array_merge(unpack("s*", $some_binary_data));

这将返回一个以 0 为基的数组

$int_list[0] = x
$int_list[1] = y
$int_list[2] = z
...

而不是 unpack 在没有提供键的情况下返回的默认以 1 为基的数组

$int_list[1] = x
$int_list[2] = y
$int_list[3] = z
...

它并不经常使用,但是只有一个参数的 array_merge() 会压缩一个按顺序排列的数字索引,从索引 [0] 开始。
Anonymous
14 年前
在处理固定宽度文件处理时,与 unpack/pack 函数相关的有用函数。
<?php
/**
* funpack
* format: 键、长度对的数组
* data: 要解包的字符串
*/
function funpack($format, $data){
foreach (
$format as $key => $len) {
$result[$key] = trim(substr($data, $pos, $len));
$pos+= $len;
}
return
$result;
}

/**
* fpack
* format: 键、长度对的数组
* data: 要打包的键、值对的数组
* pad: 填充方向
*/
function fpack($format, $data, $pad = STR_PAD_RIGHT){
foreach (
$format as $key => $len){
$result .= substr(str_pad($data[$key], $len, $pad), 0, $len);
}
return
$result;
}
?>
Sergio Santana: ssantana at tlaloc dot imta dot mx
20 年前
假设我们需要获取一个整数(例如 65)的某种内部表示形式,作为四个字节长的整数。然后,我们使用类似下面的代码:

<?
$i = 65;
$s = pack("l", $i); // 32 位长,机器字节序
echo strlen($s) . "<br>\n";
echo "***$s***<br>\n";
?>

输出为

X-Powered-By: PHP/4.1.2
Content-type: text/html

4
***A***

(即字符串 "A\0\0\0")

现在我们想从字符串 "A\0\0\0" 返回到数字 65。在这种情况下,我们可以使用

<?
$s = "A\0\0\0"; // 此字符串是数字 65 的字节表示形式
$arr = unpack("l", $s);
foreach ($arr as $key => $value)
echo "\$arr[$key] = $value<br>\n";
?>

输出为
X-Powered-By: PHP/4.1.2
Content-type: text/html

$arr[] = 65

让我们给数组键命名,例如 "mykey"。在这种情况下,我们可以使用

<?
$s = "A\0\0\0"; // 此字符串是数字 65 的字节表示形式
$arr = unpack("lmykey", $s);
foreach ($arr as $key => $value)
echo "\$arr[$key] = $value\n";
?>

输出为
X-Powered-By: PHP/4.1.2
Content-type: text/html

$arr[mykey] = 65

"unpack" 文档有点令人困惑。我认为更完整的示例可能是

<?
$binarydata = "AA\0A";
$array = unpack("c2chars/nint", $binarydata);
foreach ($array as $key => $value)
echo "\$array[$key] = $value <br>\n";
?>

其输出为

X-Powered-By: PHP/4.1.2
Content-type: text/html

$array[chars1] = 65 <br>
$array[chars2] = 65 <br>
$array[int] = 65 <br>

注意,格式字符串类似于
<格式代码> [<计数>] [<数组键>] [/ ...]

我希望这可以澄清一些内容

Sergio
ludwig at kni-online dot de
4 年前
不要忘记在解包之前解码用户定义的伪字节序列...
<?php
$byte_code_string
= '00004040';
var_dump ( unpack ( 'f', $byte_code_string ) );
?>
结果
array(1) {
[1]=>
float(6.4096905560973E-10)
}


<?php
$byte_code_string
= '00004040';
var_dump ( unpack ( 'f', hex2bin ( $byte_code_string ) ) );
?>
结果
array(1) {
[1]=>
float(3)
}
Aaron Wells
13 年前
将二进制数据转换为 PHP 数据类型的另一种方法是使用 Zend Framework 的 Zend_Io_Reader 类
http://bit.ly/9zAhgz

还有一个 Zend_Io_Writer 类可以做相反的操作。
Anonymous Coward
16 年前
警告:此 unpack 函数使数组的键从 1 开始,而不是从 0 开始。

例如
<?php
function read_field($h) {
$a=unpack("V",fread($h,4));
return
fread($h,$a[1]);
}
?>
rogier
12 年前
请注意 PHP 所在系统的行为。

在 x86 上,unpack 可能不会产生您对 UInt32 预期的结果

这是由于 PHP 的内部特性,因为整数在内部存储为 SIGNED!

对于 x86 系统,unpack('N', "\xff\xff\xff\xff") 的结果为 -1
对于 (大多数?) x64 系统,unpack('N', "\xff\xff\xff\xff") 的结果为 4294967295。

可以通过检查 PHP_INT_SIZE 的值来验证这一点。
如果此值为 4,则表示您的 PHP 在内部存储 32 位。
值为 8 表示内部存储 64 位。

为了解决这个“问题”,您可以使用以下代码来避免 unpack 的问题。
该代码用于大端字节序,但可以轻松调整为小端字节序(此外,类似的代码适用于 64 位整数)

<?php
function _uint32be($bin)
{
// $bin 是表示整数的二进制 32 位 BE 字符串
if (PHP_INT_SIZE <= 4){
list(,
$h,$l) = unpack('n*', $bin);
return (
$l + ($h*0x010000));
}
else{
list(,
$int) = unpack('N', $bin);
return
$int;
}
}
?>

请注意,您也可以使用 sprintf('%u', $x) 来显示无符号的真实值。
还要注意(至少当 PHP_INT_SIZE = 4 时),当输入大于 0x7fffffff 时,结果将是一个浮点值(只需使用 gettype 检查);

希望这能帮助到大家。
norwood at computer dot org
14 年前
从 Excel 电子表格中读取文本单元格返回的字符串包含低位嵌入的空字符:0x4100 0x4200 等等。要删除空字符,请使用

<?php
$strWithoutNulls
= implode( '', explode( "\0", $strWithNulls ) );
?>

(unpack() 在这里似乎帮助不大;需要字符来重新构成字符串,而不是整数。)
googlybash24 at aol dot com
11 年前
要将大端字节序转换为小端字节序,或将小端字节序转换为大端字节序,请使用以下方法作为示例

<?php
// file_get_contents() 返回二进制值,unpack("V*", _ ) 返回无符号长 32 位小端字节序十进制值,但如果单独使用,则之后的 bin2hex() 只是给出文件中的十六进制数据,因此我们改为使用:
// file_get_contents(),unpack("V*", _ ),然后 dechex(),按照这个顺序,就可以实现字节交换效果。
?>

使用本示例中的方法逻辑,您可以了解如何根据需要交换字节序。
sica at wnet com br
15 年前
以下脚本是一个示例,说明如何将多个值保存在文件中,并使用“\r\n”分隔它们,以及如何恢复这些值。

<?php
// 将两个整数值保存在二进制文件中
$nomearq = "./teste.bin";
$valor = 123;
$ptrarq = fopen($nomearq, "wb");
$valorBin = pack("L",$valor);
echo
"第一个值 ($valor) 使用 ";
echo
fwrite($ptrarq, $valorBin)." 字节<br>";
echo
"分隔符 \\r\\n 使用 ";
echo
fwrite($ptrarq, "\r\n")." 字节<br>";
$valor = 456;
$valorBin = pack("L",$valor);
echo
"第二个值 ($valor) 使用 ";
echo
fwrite($ptrarq, $valorBin)." 字节<br>";
fclose($ptrarq);

// 恢复保存的值
$ptrarq = fopen($nomearq, "rb");
$valorBin = file($nomearq,filesize($nomearq));
echo
"<br>读取的值是:<br>";
foreach(
$valorBin as $valor){
$valor = unpack("L",$valor);
print_r($valor);
echo
"<br>";
}
fclose($ptrarq);
?>

结果
第一个值 (123) 使用 4 字节打包
分隔符 \r\n 使用 2 字节
第二个值 (456) 使用 4 字节打包

读取的值是
Array ( [1] => 123 )
Array ( [1] => 456 )
iredden at redden dot on dot ca
24 年前
<?php

function parse_pascalstr($bytes_parsed, $parse_str) {
$parse_info = unpack("x$bytes_parsed/cstr_len", $parse_str);
$str_len = $parse_info["str_len"];
$bytes_parsed = $bytes_parsed + 1;
$parse_info = unpack("x$bytes_parsed/A".$str_len."str", $parse_str);
$str = $parse_info["str"];
$bytes_parsed = $bytes_parsed + strlen($str);

return array(
$str, $bytes_parsed);
}

?>
To Top