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
,使用 call_user_func或 call_user_func_array 函数在内部调用
pack
,以便您可以向其传递正确数量的参数。
这样的东西可能会起作用(虽然没有测试过......但你明白了总体想法)
function array_pack(array $arr) {
return call_user_func_array("pack", array_merge(array("c*"), $arr));
}
pack()
打包 bytes 时,可以通过简单地使用
chr()
、串联 .
和 foreach
循环来生成打包的二进制数据(字符串):
$packed = "";
foreach ($a as $byte) {
$packed .= chr($byte);
}
其中
$a
是源数组,$packed
是存储在字符串变量中的生成的二进制数据,根据原始问题。
在撰写本文时,已经有了 5 种不同的工作解决方案,因此值得进行基准测试,以防要打包的数据量巨大。
我使用 1048576 个元素的数组测试了五种情况,以生成 1 MB 的二进制数据。我测量了执行时间和消耗的内存。
测试环境:
PHP 8.2.7
- macOS Ventura
- CPU Apple M1 Pro
(当然只使用单核)
// pack with ... operator: 9 ms
// string concatentation: 10 ms
// call_user_func_array: 12 ms
// multiple pack: 46 ms
// array_reduce: 10918 ms
测试环境:
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";
我使用这个功能:
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);
});