下表是手冊上的,光看這個表還不知道如何用,我在後面添加了解釋,應該把解釋中的代碼都跑一遍,就全明白了。
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相當於遊標移動到絕對位置
|
每次pack和unpack都需要去查表,而且php對於大小端的支持也不完善,下面是我寫的一個類,流式讀寫,完全支持大小端,用起來應該方便不少。
<?php
/**
* Copyright (c) 2016, bookrpg, All rights reserved.
* @author llj <[email protected]>
* @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;