将PHP数组传递给pack()函数

12

pack() 语法如下(来自 http://php.net/manual/zh/function.pack.php):

string pack ( string $format [, mixed $args [, mixed $... ]] )

因此,假设我需要打包三个字节:

$packed = pack( "c*", 65, 66, 67 );

但如果我需要打包任意数量的字节呢?

它们可以方便地存储到数组中,所以我天真地尝试了以下代码:

$a = array( 65, 66, 67 );
$packed = pack( "c*", $a );
但它不起作用。 有没有办法让 pack() 与数组一起使用?

2
我认为这个链接会对你有所帮助:https://dev59.com/gG035IYBdhLWcg3wW-pa#5473057 - Rizier123
当一个针对PHP的回答工作正常时,却因为在问题中没有说明PHP5.6而在随后给出了一个PHP5.6+特定的答案而被拒绝,这并不好:)...我的你的巫毒娃娃几乎完成了。 - inquam
5个回答

25

虽然有点晚了,但以后可以参考一下使用新的...操作符(v5.6+)来内联地展开数组:

$packed = pack("c*", ...$a);

3
您可以创建自己的函数array_pack,该函数内部使用call_user_funccall_user_func_array函数调用pack,以便您可以向其传递正确数量的参数。可能会有类似这样的东西(尚未测试...但您可以获得一般想法)。
function array_pack(array $arr) {
  return call_user_func_array("pack", array_merge(array("c*"), $arr));
}

3

使用字符串拼接代替pack()

当需要打包字节时,可以通过使用chr()、拼接符号.以及foreach循环来生成打包后的二进制数据(字符串):

packed = "";
foreach ( $a as $byte ) {
    $packed .= chr( $byte );
}

在原始问题中,$a是源数组,$packed是存储在字符串变量中的生成的二进制数据。


基准测试

在编写本文时,已经有5种不同的工作解决方案,因此值得进行基准测试,以防需要打包的数据量很大。

我使用了1048576个元素的数组来测试这五种情况,以生成1 MB的二进制数据。我测量了执行时间和消耗的内存。

测试环境:PHP 5.6.30 - Mac OS X - 2.2 GHz Intel Core I7

当然只使用了单个核心

// pack with ... operator:    57 ms - 1.3 MB
// string concatentation:    197 ms - 1.3 MB
// call_user_func_array:     249 ms - 1.5 MB
// multiple pack:            298 ms - 1.3 MB
// array_reduce:           39114 ms - 1.3 MB

直接使用...运算符与pack函数结合是目前最快的解决方案(已接受答案)。
如果没有...可用(PHP 5.6之前的版本),则由此答案提出的解决方案(字符串连接)是最快的。
每种情况的内存使用几乎相同。
如果有人感兴趣,我会发布测试代码。
<?php

// Return elapsed time from epoch time in milliseconds

function milliseconds() {
    $mt = explode(' ', microtime());
    return ((int)$mt[1]) * 1000 + ((int)round($mt[0] * 1000));
}



// Which test to run [1..5]

$test = $argv[ 1 ];



// Test 1024x1024 sized array

$arr = array();
for( $i = 0; $i < 1024 * 1024; $i++ )
{
    $arr[] = rand( 0, 255 );
}



// Initial memory usage and time

$ms0 = milliseconds();
$mem0 = memory_get_usage( true );



// Test 1: string concatentation

if( $test == '1' )
{
    $data = "";
    foreach ( $arr as $byte ) {
        $data .= chr( $byte );
    }
            
    $test = "string concatentation";
}



// Test 2: call_user_func_array

if( $test == '2' )
{
    $data = call_user_func_array("pack", array_merge(array("c*"), $arr));

    $test = "call_user_func_array";
}



// Test 3: pack with ... operator

if( $test == '3' )
{
    $data = pack("c*", ...$arr);

    $test = "pack with ... operator";
}



// Test 4: array_reduce

if( $test == '4' )
{
    $data = array_reduce($arr, function($carry, $item) { return $carry .= pack('c', $item); });

    $test = "array_reduce";
}



// Test 5: Multiple pack

if( $test == '5' )
{
    $data = "";
    foreach ($arr as $item) $data .= pack("c", $item);

    $test = "multiple pack";
}



// Output result

$ms = milliseconds() - $ms0;
$mem = round( ( memory_get_usage( true ) - $mem0 ) / ( 1024 * 1024 ), 1 );
echo "$test: $ms ms; $mem MB\n";

另一种完整性的变体(而不是foreach):$packed = implode(array_map('chr', $a)); - undefined

1
我使用这个函数:

private function pack_array($format, $arg)
{
    $result="";
    foreach ($arg as $item) $result .= pack ($format, $item);
    return $result;
}

0

如果您无法使用...运算符,另一个选择是:

$packed = array_reduce($a, function($carry, $item) {
    return $carry .= pack('c', $item);
});

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接