下表是手册上的,光看这个表还不知道如何用,我在后面添加了解释,应该把解释中的代码都跑一遍,就全明白了。
a | NUL-padded string | $data = pack("a4", 'abc'); echo bin2hex($data) . PHP_EOL;//61626300 61、62、63、00都是十六进制的,分别代表一个字节 a后面要跟一个数字,代表要写入的字符串的长度,不够就补0 |
A | SPACE-padded string | 同上,不够就补空格,空格的ascii码是32(0x20) |
h | Hex string, low nibble first | $data = pack("h3", 'abc'); echo bin2hex($data) . PHP_EOL;//ba0c $data = pack("h3", 'xyz'); echo bin2hex($data) . PHP_EOL;//报错 |
H | Hex string, high nibble first | $data = pack("H3", 'abc'); echo bin2hex($data) . PHP_EOL;//abc0 $data = pack("H3", 'xyz'); echo bin2hex($data) . PHP_EOL;//报错 |
c | signed char | $data = pack("ccc", 0x61, 0x62, 0x63); echo bin2hex($data) . PHP_EOL;//616263 signed char和unsigned char其实就是int8和uint8,所以后面的参数是0x61, 0x62, 0x63。 |
C | unsigned char | |
s | signed short (always 16 bit, machine byte order) | int16,大小端依赖当前机器 |
S | unsigned short (always 16 bit, machine byte order) | uint16,大小端依赖当前机器 |
n | unsigned short (always 16 bit, big endian byte order) | uint16,大端 |
v | unsigned short (always 16 bit, little endian byte order) | uint16,小端 |
i | signed integer (machine dependent size and byte order) | int,字节数和大小端都依赖当前机器 |
I | unsigned integer (machine dependent size and byte order) | uint,字节数和大小端都依赖当前机器 |
l | signed long (always 32 bit, machine byte order) | int32,大小端依赖当前机器 |
L | unsigned long (always 32 bit, machine byte order) | uint32,大小端依赖当前机器 |
N | unsigned long (always 32 bit, big endian byte order) | uint32,大端 |
V | unsigned long (always 32 bit, little endian byte order) | uint32,小端 |
q | signed long long (always 64 bit, machine byte order) | int64,大小端依赖当前机器 |
Q | unsigned long long (always 64 bit, machine byte order) | uint64,大小端依赖当前机器 |
J | unsigned long long (always 64 bit, big endian byte order) | uint64,大端 |
P | unsigned long long (always 64 bit, little endian byte order) | uint64,小端 |
f | float (machine dependent size and representation) | float,字节数和浮点位数都依赖当前机器(一般都是ieee标准32位) |
d | double (machine dependent size and representation) | double,字节数和浮点位数都依赖当前机器(一般都是ieee标准64位) |
x | NUL byte | $data = pack("Cx2", 0x61); echo bin2hex($data) . PHP_EOL;//610000
x2代表写入2个空字节(0x00)
对于unpack相当于游标向前或向后移动n个位置
|
X | Back up one byte | $data = pack("CCCX", 0x61,0x62,0x63); echo bin2hex($data) . PHP_EOL;//616200
X把最后一个字节0x63被吃掉了
对于unpack相当于游标回退一个位置
|
Z | NUL-padded string (new in PHP 5.5) | $data = pack("a3", 'abc');
echo bin2hex($data) . PHP_EOL;//616263
echo bin2hex($data) . PHP_EOL;//616200
echo bin2hex($data) . PHP_EOL;//6162300
|
@ | NUL-fill to absolute position | $data = pack("cc@4", 0x61, 0x62);
echo bin2hex($data) . PHP_EOL;//61620000
echo bin2hex($data) . PHP_EOL;//61
@1表示后面补0直到第1位(从1开始),第1位已经有数字了(0x61),所以就不需要补0了
对于unpack相当于游标移动到绝对位置
|
把同一格式应用于所有的元素则加个*号,如:
$data = pack ("C*", 0x61, 0x62, 0x63);
echo bin2hex($data) . PHP_EOL;//616263
对于unpack:
$data = pack ("a1a1a1", 'a', 'b', 'c');
$arr = unpack("a1kx/a1ky/a1kz", $data);
print_r($arr) . PHP_EOL;
// Array
// (
// [kx] => a
// [ky] => b
// [kz] => c
// )
不能直接使用pack的格式化字符串"a1a1a1",而必须在每个项后面跟上解析出来的key,key不能用数字开头,然后用/分割。
每次pack和unpack都需要去查表,而且php对于大小端的支持也不完善,下面是我写的一个类,流式读写,完全支持大小端,用起来应该方便不少。
测试代码:
每次pack和unpack都需要去查表,而且php对于大小端的支持也不完善,下面是我写的一个类,流式读写,完全支持大小端,用起来应该方便不少。
<?php
/**
* Copyright (c) 2016, bookrpg, All rights reserved.
* @author llj <wwwllj1985@163.com>
* @license The MIT License
*/
//namespace bookrpg\util;
class Endian
{
const BIG_ENDIAN = 'bigEndian';
const LITTLE_ENDIAN = 'littleEndian';
}
class ByteArray
{
private $data = '';
private $position = 0;
private $endian;
private $isLittleEndian;
public $needConvertEndian;
private static $systemEndian = null;
public static function systemEndian()
{
if(self::$systemEndian === null){
self::$systemEndian = pack('v', 1) == pack('s', 1) ?
Endian::LITTLE_ENDIAN : Endian::BIG_ENDIAN;
}
return self::$systemEndian;
}
public function __construct($data = null)
{
$this->setEndian(self::systemEndian());
$this->data = is_null($data) ? $this->data : $data;
}
/*
* Endian::LITTLE_ENDIAN or Endian::BIG_ENDIAN
*/
public function getEndian()
{
return $this->endian;
}
public function setEndian($value)
{
$this->endian = $value == Endian::BIG_ENDIAN ? Endian::BIG_ENDIAN : Endian::LITTLE_ENDIAN;
$this->isLittleEndian = $this->endian == Endian::LITTLE_ENDIAN;
$this->needConvertEndian = $this->endian != self::systemEndian();
}
public function getLength()
{
return strlen($this->data);
}
public function getPosition()
{
return $this->position;
}
public function setPosition($value)
{
$this->position = $value;
}
public function getBytesAvailable()
{
return strlen($this->data) - $this->position;
}
public function clear()
{
$this->data = '';
$this->position = 0;
}
public function readBoolean()
{
if($this->getBytesAvailable() < 1){
return null;
}
$arr = unpack('@' . $this->position . '/ck', $this->data);
$this->position++;
return boolval($arr['k']);
}
public function readByte()
{
if($this->getBytesAvailable() < 1){
return false;
}
$arr = unpack('@' . $this->position . '/ck', $this->data);
$this->position++;
return $arr['k'];
}
public function readUByte()
{
if($this->getBytesAvailable() < 1){
return false;
}
$arr = unpack('@' . $this->position . '/Ck', $this->data);
$this->position++;
return $arr['k'];
}
public function readInt16()
{
//php缺少有符号型整数的大小端读取,参见补码相关知识
if(($i = $this->readUInt16()) !== false && $i > 0x7fff){
$i = -(~($i - 1) & 0xffff);
}
return $i;
}
public function readUInt16()
{
if($this->getBytesAvailable() < 2){
return false;
}
$key = $this->needConvertEndian ? ($this->isLittleEndian ? '/vk' : '/nk') : '/Sk';
$arr = unpack('@' . $this->position . $key, $this->data);
$this->position += 2;
return $arr['k'];
}
public function readInt32()
{
if(($i = $this->readUInt32()) !== false && $i > 0x7fffffff){
$i = -(~($i - 1) & 0xffffffff);
}
return $i;
}
public function readUInt32()
{
if($this->getBytesAvailable() < 4){
return false;
}
$key = $this->needConvertEndian ? ($this->isLittleEndian ? '/Vk' : '/Nk') : '/Lk';
$arr = unpack('@' . $this->position . $key, $this->data);
$this->position += 4;
return $arr['k'];
}
public function readInt64()
{
if(($i = $this->readUInt64()) !== false && $i > 0x7fffffffffffffff){
$i = -(~($i - 1));
}
return $i;
}
/**
* php has't uint64,so be sure the number is in int64.min ~ int64.max
* @return [type] [description]
*/
public function readUInt64()
{
if($this->getBytesAvailable() < 8){
return false;
}
$key = $this->needConvertEndian ? ($this->isLittleEndian ? '/Pk' : '/Jk') : '/Qk';
$arr = unpack('@' . $this->position . $key, $this->data);
$this->position += 8;
return $arr['k'];
}
public function readFloat()
{
if($this->getBytesAvailable() < 4){
return false;
}
if($this->needConvertEndian){
$data = $this->readBytes(4);
$arr = unpack('fk', strrev($data));
} else{
$arr = unpack('@' . $this->position . '/fk', $this->data);
$this->position += 4;
}
return $arr['k'];
}
public function readDouble()
{
if($this->getBytesAvailable() < 8){
return false;
}
if($this->needConvertEndian){
$data = $this->readBytes(8);
$arr = unpack('dk', strrev($data));
} else{
$arr = unpack('@' . $this->position . '/dk', $this->data);
$this->position += 8;
}
return $arr['k'];
}
public function readBytes($count)
{
if($this->getBytesAvailable() < $count){
return false;
}
$key = '/a'. $count . 'k';
$arr = unpack('@' . $this->position . $key, $this->data);
$this->position += $count;
return $arr['k'];
}
/**
* first read strlen(2byte), then read str
*/
public function readString()
{
$len = $this->readUInt16();
if($len <=0 || $this->getBytesAvailable() < $len){
return false;
}
$key = '/a'. $len . 'k';
$arr = unpack('@' . $this->position . $key, $this->data);
$this->position += $len;
return $arr['k'];
}
public function writeBoolean($value)
{
$this->data .= pack('c', $value ? 1 : 0);
$this->position++;
}
public function writeByte($value)
{
$this->data .= pack('c', $value);
$this->position++;
}
public function writeUByte($value)
{
$this->data .= pack('C', $value);
$this->position++;
}
public function writeInt16($value)
{
//php缺少有符号型整数的大小端写入,参见补码相关知识
if($value < 0){
$value = -(~($value & 0xffff) + 1);
}
$this->writeUInt16($value);
}
public function writeUInt16($value)
{
$key = $this->needConvertEndian ? ($this->isLittleEndian ? 'v' : 'n') : 'S';
$this->data .= pack($key, $value);
$this->position += 2;
}
public function writeInt32($value)
{
if($value < 0){
$value = -(~($value & 0xffffffff) + 1);
}
$this->writeUInt32($value);
}
public function writeUInt32($value)
{
$key = $this->needConvertEndian ? ($this->isLittleEndian ? 'V' : 'N') : 'L';
$this->data .= pack($key, $value);
$this->position += 4;
}
public function writeInt64($value)
{
if ($value < 0) {
$value = -(~$value + 1);
}
$this->writeUInt64($value);
}
/**
* php has't uint64,so be sure the number is in int64.min ~ int64.max
* @return [type] [description]
*/
public function writeUInt64($value)
{
$key = $this->needConvertEndian ? ($this->isLittleEndian ? 'P' : 'J') : 'Q';
$this->data .= pack($key, $value);
$this->position += 8;
}
public function writeFloat($value)
{
$this->data .= $this->needConvertEndian ? strrev(pack('f', $value)) : pack('f', $value);
$this->position += 4;
}
public function writeDouble($value)
{
$this->data .= $this->needConvertEndian ? strrev(pack('d', $value)) : pack('d', $value);
$this->position += 8;
}
public function writeBytes($value)
{
$len = strlen($value);
$this->data .= pack('a' . $len, $value);
$this->position += $len;
}
/**
* first read strlen(2byte), then read str
*/
public function writeString($value)
{
$len = strlen($value);
$this->writeUInt16($len);
$this->data .= pack('a' . $len, $value);
$this->position += $len;
}
public function toBytes()
{
return $this->data;
}
}
测试代码:
$byte = new ByteArray();
$byte->setEndian(Endian::BIG_ENDIAN);
$byte->writeUByte(111);
$byte->writeBoolean(true);
$byte->writeUInt16(21);
$byte->writeUInt32(21);
$byte->writeUInt64(21);
$byte->writeFloat(-1.1);
$byte->writeDouble(-1.1);
$byte->writeString('a你好');
$byte->writeBytes('aaa');
$byte->setPosition(0);
echo $byte->readUByte() . PHP_EOL;
echo $byte->readBoolean() . PHP_EOL;
echo $byte->readUInt16() . PHP_EOL;
echo $byte->readUInt32() . PHP_EOL;
echo $byte->readUInt64() . PHP_EOL;
echo $byte->readFloat() . PHP_EOL;
echo $byte->readDouble() . PHP_EOL;
echo $byte->readString() . PHP_EOL;
echo $byte->readBytes(3) . PHP_EOL;