pack、unpack函數,如果沒有接觸過socket,這個可能會比較陌生,這兩個函數在socket交互的作用是組包,將數據裝進一個二進制字符串,和對二進制字符串中的數據進行解包,這個裡面有好多種格式,具體的格式可以去查查官方的手冊(或者等看完本篇文章之後,去調用接口查看),我這裡主要用了pack(“N”,int),pack(“a”,str)以及他們兩個對應的解包函數,N在手冊中的解釋是下面這個,占4個字節,大端方式(其實就是低位在前還是在後的問題)。a是對字符串進行打包,不夠指定的數值的時候用NULL(\0,或者說assic碼0對應的字符)填充。
N - unsigned long (always 32 bit, big endian byte order)
a - NUL-padded string
我將用這個打包解包函數做一個函數手冊查詢小工具,或者可以說是一個自制小型二進制數據庫。
在做這個二進制文件數據庫的時候我會創建兩個文件,一個是索引文件,一個是要查詢的數據的文件,分別看看他們的結構:
說明中括號內的數字為所占字節(bytes)數,"~"波浪線表示所占字節數不確定
數據文件,第一個php是一個正式的字符串"php",占4個字節,後面跟著版本說明,長度不確定(這個長度可以從後面的index文件中獲取),接下來後面是存儲信息的主體了。首先是一個函數名長度lenName占4個字節,接下來是函數名稱,長度不確定,有前面的lenName對應的值確定,接下來是lenVal占4個字節,後面跟的是具體的函數說明內容,長度有前面的lenVal對應的值確定。
內容存儲格式定義
++++++++++++++++++++++++++++++++++++++
|php(4) |版本說明(~) |
++++++++++++++++++++++++++++++++++++++
|lenName(4) |函數名稱(~) |
++++++++++++++++++++++++++++++++++++++
|lenVal(4) |函數內容(~) |
++++++++++++++++++++++++++++++++++++++
......
索引文件,索引文件就比較簡單了,其中全部存儲了上面的存儲文件中每個函數開始的指針位置,每個位置占用4個字節。
索引格式定義
++++++++++++++++++++++++++++++++++++++
|position(4) |
++++++++++++++++++++++++++++++++++++++
......
由於存儲文件中的內容是按照函數名順序排序存儲的,索引也是按照函數存儲的順序存儲的,所以獲取起來很方便,直接使用二分法就可以很輕松的獲取到想要的函數
在查詢的時候主要使用了下面幾個方法:
第一、從制定位置獲取一條索引的值(也就是對應的函數存儲文件的指針位置)
/**
* 從索引文件中獲取一條記錄的位置
* @param 索引文件中的開始位置,從開始位置獲取四個字節為一個函數說明的開始位置
* @return 返回該索引位置所對應的存儲位置指針偏移量
*/
private function _getOneIndex($pos)
{
fseek($this->_indexHandle, $pos);
$len = unpack("Nlen", fread($this->_indexHandle, 4));
return $len['len'];
}
第二、從指定的指針偏移位置獲取一條len(4)+val(~)格式的內容
/**
* 從制定的指針偏移量獲取一個len+val型的內容
* @param $pos 文件的指針偏移量
* @return 返回數組,包括長度和值
*/
private function _getStoreLenValFormat($pos){
fseek($this->_storeHandle, $pos);
$len = unpack("Nlen", fread($this->_storeHandle, 4));
$len = $len['len'];
$val = fread($this->_storeHandle, $len);
return array
(
'len' => $len,
'value' => $val,
);
}
第三、獲取制定函數的說明,這個也是最主要的一部分,使用二分法從數據文件中獲取一條記錄
/**
* 獲取函數內容
* @param 要查找的函數名稱
* @return 返回函數說明的json字符串
*/
public function get($func)
{
if(!$this->isInit())
return;
$begin = 0;
$end = filesize($this->_indexFile)/4;
$ret = '[]';
while($begin < $end){
$mid = floor(($begin + $end)/2);
$pos = $mid*4; //$mid只是指針變量的位置,還需要乘上指針的長度4
$pos = $this->_getOneIndex($pos);
$name = $this->_getStoreLenValFormat($pos);
$flag = strcmp($func, $name['value']);
if($flag == 0){
$val = $this->_getStoreLenValFormat($pos+4+$name['len']);
$ret = $val['value'];
break;
}elseif($flag < 0){
$end = $end == $mid ? $mid-1 : $mid;
}else{
$begin = $begin == $mid ? $mid+1 : $mid;
}
}
return $ret;
}
使用很簡單,只需包含類庫文件和存儲文件數據庫,然後調用幾句代碼就可以
<?php
include_once("./manual/phpManual.php");
$t = new phpManual();
$t->init('zh');
echo $t->get("unpack");
輸出的是json字符串,轉化後如下所示,其中有詳細的說明,以及簡潔的例子
{
"name": "unpack",
"desc": "Unpack data from binary string.",
"long_desc": "Unpacks from a binary string into an array according to the given `format`.\\n\\nThe unpacked data is stored in an associative array. To accomplish this you have to name the different format codes and separate them by a slash /. If a repeater argument is present, then each of the array keys will have a sequence number behind the given name.",
"ver": "PHP 4, PHP 5",
"ret_desc": "Returns an associative array containing unpacked elements of binary string.",
"seealso": [
"pack"
],
"url": "function.unpack",
"class": null,
"params": [
{
"list": [
{
"type": "string",
"var": "$format",
"beh": 0,
"desc": "See pack() for an explanation of the format codes."
},
{
"type": "string",
"var": "$data",
"beh": 0,
"desc": "The packed data."
}
],
"ret_type": "array"
}
],
"examples": [
{
"title": "unpack() example",
"source": "$binarydata = \"\\x04\\x00\\xa0\\x00\";\n$array = unpack(\"cchars/nint\", $binarydata);",
"output": null
},
{
"title": "unpack() example with a repeater",
"source": "$binarydata = \"\\x04\\x00\\xa0\\x00\";\n$array = unpack(\"c2chars/nint\", $binarydata);",
"output": null
},
{
"title": "unpack() example with unnamed keys",
"source": "$binarydata = \"\\x32\\x42\\x00\\xa0\";\n$array = unpack(\"c2/n\", $binarydata);\nvar_dump($array);",
"output": null
}
]
}
最後再附上目錄結構:
+phpManual
+manual
+phpManual
+zh
|_manualIndex
|_manualStore
|_phpManual.php
|_test.php
這個是程序的完整地址:
完整例子地址
https://github.com/aizuyan/php-doc-parser 從這裡拿到的phpmanual的全部數據
本文版權歸作者iforever(luluyrt@163.com)所有,未經作者本人同意禁止任何形式的轉載,轉載文章之後必須在文章頁面明顯位置給出作者和原文連接,否則保留追究法律責任的權利。