1. 程式人生 > >pack和unpack格式化字串(format string)解釋

pack和unpack格式化字串(format string)解釋

不能直接使用pack的格式化字串"a1a1a1",而必須在每個項後面跟上解析出來的key,key不能用數字開頭,然後用/分割。


每次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;


相關推薦

packunpack格式化字串(format string)解釋

不能直接使用pack的格式化字串"a1a1a1",而必須在每個項後面跟上解析出來的key,key不能用數字開頭,然後用/分割。 每次pack和unpack都需要去查表,而且php對於大小端的支援也不完善,下面是我寫的一個類,流式讀寫,完全支援大小端,用起來應該方便不少。<?php /** * Copy

pytorch的packunpack函式

pack unpack 匯入 from torch.nn.utils.rnn import pack_padded_sequence as pack from torch.nn.utils.rnn import pad_packed_sequence as unpack 使

perl packunpack的使用詳解

perl pack和unpack的使用方法Pack 與unpack使用說明: pack可視為將一系列的片段的數值打包在一起,可用於對dev檔案、socket、memory的讀寫,因為這些需要一塊完整的memory,而且需要事先打包成特定格式;而unpack可以視為將將這些完

格式化字串-String.format()的使用【Java】

常規型別的格式化 String類的format()方法用於建立格式化的字串以及連線多個字串物件。熟悉C語言的同學應該記得C語言的sprintf()方法,兩者有類似之處。format()方法有兩種過載形式。 format(String format, Object… args) 新字串

格式化字串漏洞 format string exploit(一)

    本文系原創,轉載請說明出處 本文為基於CTF WIKI的PWN學習 0x00 格式化字串原理    先附一張經典的圖,如下     其棧上佈局如下: some value  3.14 123456 addr of "red

[Python開發]Python中struct.pack()struct.unpack()用法詳細說明

python中的struct主要是用來處理C結構資料的,讀入時先轉換為Python的字串型別,然後再轉換為Python的結構化型別,比如元組(tuple)啥的~。一般輸入的渠道來源於檔案或者網路的二進位制流。   1.struct.pack()和struct.unpack() 在轉化

js字串格式化方法format

/** * 設定字串format函式 * 例子: '{0}{1}.format(5,6)' */ setStringFormat() { String.prototype['format'] = function () { const e = arguments; retu

Java_51_組合_內部類詳解_字串String類)_equals==的區別

組合 使用組合,可以獲得更多的靈活性,你甚至可以在執行的時候才決定哪幾個類組合在一起。 使用繼承,他是一種高度耦合,派生類和基類被緊緊的綁在一起,靈活性大大降低,而且,濫用繼承,也會使繼承樹變得又大又複雜,很難理解和維護。 如果是is-a關係,用繼承。【是一個[物件]】 如果是h

Python format格式化字串(轉)

轉自: https://www.cnblogs.com/wilber2013/p/4641616.html  # 位置引數 print("{0} is {1} years old".format("Wilber", 28)) print("{} is {} years old".

PHP字串處理時間格式化整理

一、PHP字串相關 1、字串擷取(開始位置、長度) echo substr("Hello world",0,10)."<br>";      //Hello worlecho substr("Hello world",0,-1)."<br>";      //Hello worle

python字串格式化format

用法:   它通過{}和:來代替傳統%方式 1、使用位置引數 要點:從以下例子可以看出位置引數不受順序約束,且可以為{},只要format裡有相對應的引數值即可,引數索引從0開,傳入位置引數列表可用*列表 >>> li = ['hoho',18] >>> 'm

C++ primer plus書之--C++函式C語言字串, 結構體, string

函式和C風格字串 要將C風格字串作為引數傳遞給函式, 表示字串的方式有三種: 1.char陣列 2.用""擴起來的字串常量 3.被設定為字串地址的char指標 來看一個例子: // c風格字串例子 #include "iostream" using namespace std

Python格式化字串str.format()

Python 字串格式化解決的問題:    字串格式化是為了實現字串和變數同時輸出時按一定的格式顯示。   例如:" 一年有{}天,一天有{}小時 。".format(365,24)  ==>   " 一年有365天,一天有24小時。"   fo

2018年12月7日 字串格式化2 format與函式1

tp7="i am \033[44;1m %(name)-25.6s\033[0m"%{"name":"sxj2343333"} print(tp7) #-為左對齊,\033[44;1m \033[0m 為選取44色號的顏色   tp1="I am {},age{}

julia:String:轉化輸出格式化

# String: 轉化和格式化 # strings can be converted using float and Int e_str1 = "2.718" e = float(e_str1) println(5*e) #> 13.5914 num_1

Python用format格式化字串

format是是python2.6新增的一個格式化字串的方法,相對於老版的%格式方法,它有很多優點。 1.不需要理會資料型別的問題,在%方法中%s只能替代字串型別 2.單個引數可以多次輸出,引數順序可以不相同 3.填充方式十分靈活,對齊方式十分強大 4.官方推

Python學習day05-字串格式化format字串格式化、函式(上)

一、字串格式化 ①佔位符: %s:萬能的,可以列印所有資料型別,一般用於字串 %d:列印整型 %f:列印浮點數,預設最多列印小數點後6位 ②格式化輸出: (1)普通輸出 msg='i am %s my age is %d' % ('Hello',18) prin

Python中struct pack struct unpack 用法詳細說明

                python中的struct主要是用來處理C結構資料的,讀入時先轉換為Python的字串型別,然後再轉換為Python的結構化型別,比如元組(tuple)啥的~。一般輸入的渠道來源於檔案或者網路的二進位制流。1.struct.pack()和struct.unpack()在轉化過

Python格式化字串輸出(%與format用法)

一、%用法: 字串格式程式碼如下: 符號 說明 補充 %s 字串   %c 字元   %d 十進位制

C++ format 格式化字串實現方式

1.You can't do it directly, because you don't have write access to the underlying buffer (until C++11; see Dietrich Epp's comment). You'll