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()
работать с массивом?
Немного опоздал на вечеринку, но для дальнейшего использования, вы можете использовать новый ...
оператор (v5.6 +), чтобы взорвать массив в строке:
$packed = pack("c*", ...$a);
Вы можете создать свою собственную функцию 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));
}
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";
Я использую эту функцию:
private function pack_array($format, $arg)
{
$result="";
foreach ($arg as $item) $result .= pack ($format, $item);
return $result;
}
Другой вариант, если вы не можете использовать ...
оператор:
$packed = array_reduce($a, function($carry, $item) {
return $carry .= pack('c', $item);
});