web-dev-qa-db-ja.com

PHP配列をpack()関数に渡す

pack()構文は(from http://php.net/manual/en/function.pack.php

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

したがって、3バイトをパックする必要があると仮定します

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

しかし、任意のバイト数をパックする必要がある場合はどうなりますか?

それらは便利に配列に格納できるので、私は素朴に試しました

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

しかし、それは機能しません。

pack()を配列で機能させる方法はありますか?

6
Paolo

パーティーに少し遅れましたが、将来の参考のために、新しい...演算子(v5.6 +)を使用して、配列をインラインで展開できます。

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

pack()の代わりに文字列連結を使用します

bytesをパックする場合、パックされたバイナリデータ(文字列)は、chr()、連結.、およびforeachループ:

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

ここで、$aはソース配列であり、$packedは、元の質問に従って、文字列変数に格納された生成されたバイナリデータです。


基準

これを書いている時点で、5つの異なる実用的なソリューションが用意されているので、パックするデータの量が膨大な場合に備えてベンチマークを行う価値があります。

1 MBのバイナリデータを生成するために、1048576要素の配列を使用して5つのケースをテストしました。実行時間と消費メモリを測定しました。

テスト環境: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関数で直接使用されます(受け入れられた回答

...が利用できない場合(5.6より前のPHPバージョン)この回答string concatentation)が最速です。

メモリ使用量はすべての場合でほぼ同じです。

興味があればテストコードを投稿します。


<?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";
2
Paolo

独自の関数を作成できますarray_packcall_user_func または call_user_func_array 関数を使用して内部的にpackを呼び出すため、正しい数のパラメーターを渡すことができます。

このようなものはおそらく機能する可能性があります(ただし、テストされていません...しかし、一般的な考え方はわかります)

function array_pack(array $arr) {
  return call_user_func_array("pack", array_merge(array("c*"), $arr));
}
2
inquam

私はこの関数を使用します:

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

...演算子を使用できない場合の別のオプション:

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