PHP Передача массива в функцию pack()

pack() синтаксис (из http://php.net/manual/en/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() работать с массивом?


person Paolo    schedule 28.04.2015    source источник
comment
Я думаю, это должно вам помочь: stackoverflow.com/a/5473057/3933332   -  person Rizier123    schedule 28.04.2015
comment
Нехорошо не принимать рабочий ответ для php, когда конкретный ответ php5.6+ был дан позже, когда php5.6 не был указан в вопросе :)... Моя кукла вуду почти завершена.   -  person inquam    schedule 20.08.2016


Ответы (5)


Немного поздно для вечеринки, но для дальнейшего использования вы можете использовать новый оператор ... (v5.6+), чтобы взорвать массив в строке:

$packed = pack("c*", ...$a);
person Alex Howansky    schedule 19.08.2016

Вы можете создать свою собственную функцию array_pack, которая внутренне вызывает pack, используя call_user_func или функции call_user_func_array, чтобы вы могли передать правильное количество параметров к нему.

Что-то вроде этого, вероятно, могло бы работать (хотя и не тестировалось... Но вы поняли общую идею)

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

использовать конкатенацию строк вместо pack()

При упаковке байтов упакованные двоичные данные (строка) могут быть созданы простым использованием chr(), конкатенации . и цикла foreach:

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

Где $a — исходный массив, а $packed — созданные двоичные данные, хранящиеся в строковой переменной, в соответствии с исходным вопросом.


ориентир

Имея на момент написания статьи 5 различных рабочих решений, стоит провести тест на случай, если объем данных для упаковки огромен.

Я протестировал пять случаев с массивом из 1048576 элементов, чтобы получить 1 МБ двоичных данных. Я измерил время выполнения и потребляемую память.

Среда тестирования: 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), решение, предложенное этим ответом ( 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";
person Paolo    schedule 15.04.2018

Я использую эту функцию:

private function pack_array($format, $arg)
{
    $result="";
    foreach ($arg as $item) $result .= pack ($format, $item);
    return $result;
}
person Rafael Hernández    schedule 16.07.2015

Другой вариант, если вы не можете использовать оператор ...:

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