高效還是炫技?PHP解析二進制文件,就靠這倆祖傳的函數,我——

引言

PHP幾乎很少處理二進制文件。但是便宜也完整的保留了這個功能。當你需要的時候,PHP自帶的pack() & unpack()能能夠極大地提供便利。下面我們從一個編程問題開始,討論二進制文件的操作。

下文討論gif文件,我們會編寫一個函數,處理的內容跟GIF圖像後綴無關。當然,我們也不打算嘗試PHP的GD庫。

高效還是炫技?PHP解析二進制文件,就靠這倆祖傳的函數,我——

gif文件頭

不使用任何與圖像處理相關的函數,為了解決這個問題,我們得從GIF文件本身獲取數據。

與HTML、XML或其他文本格式文件不同,GIF文件和大多數其他圖像格式是以二進制格式存儲的。

大多數二進制文件的頂部都有一個頭文件,它提供關於特定文件的元信息。我們可以使用這些信息來查找文件的類型和其他信息,比如GIF文件的高度和寬度。

下面顯示了一個典型的原始GIF頭文件,使用的是十六進制編輯器。

標題的詳細描述如下。

高效還是炫技?PHP解析二進制文件,就靠這倆祖傳的函數,我——

因此,要檢查圖像文件是否是有效的GIF,我們需要檢查文件的頭3個字節,它有“GIF”標記,然後3個字節,它給出了版本號;“87a”或“89a”。

對於這樣的需求,unpack()函數是必不可少的。在查看解決方案之前,我們快速查看一下unpack()函數本身。

使用unpack()函數

unpack()是pack()的補充——它根據指定的格式將二進制數據轉換為關聯數組。

它有點類似於sprintf,根據給定的格式轉換字符串數據。

這兩個函數(pack & unpack),允許我們根據指定的格式字符串讀寫二進制數據緩衝區。這使編程人員能夠輕鬆地與用其他語言,或其他格式編寫的程序交換數據。

舉個小例子:

<code>$data = unpack('C*', 'codediesel');
var_dump($data);/<code>

這會打印以下十進制代碼的“codediesel”:

<code>array
1 => int 99
2 => int 111
3 => int 100
4 => int 101
5 => int 100

6 => int 105
7 => int 101
8 => int 115
9 => int 101
10 => int 108/<code>

在上面的例子中,第一個參數是格式字符串,第二個參數是實際數據。

格式字符串指定應該如何解析數據參數。在本例中,格式“C”的第一部分指定我們應該將數據的第一個字符視為無符號字節。下一部分' * '告訴函數將前面指定的格式代碼應用於所有剩餘的字符。

乍一看上去挺混亂,但是慢慢梳理你會發現其中的規律。下一節我們提供一個具體的例子。


抓取頭部數據

下面是使用unpack()函數解決GIF問題的方法。如果給定的文件是GIF格式的,則is_gif()函數將返回true。

<code>function is_gif($image_file)
{

/* Open the image file in binary mode */
if(!$fp = fopen ($image_file, 'rb')) return 0;

/* Read 20 bytes from the top of the file */
if(!$data = fread ($fp, 20)) return 0;

/* Create a format specifier */
$header_format = 'A6version'; # Get the first 6 bytes

/* Unpack the header data */
$header = unpack ($header_format, $data);

$ver = $header['version'];

return ($ver == 'GIF87a' || $ver == 'GIF89a')? true : false;

}

/* Run our example */
echo is_gif("aboutus.gif");/<code>

需要注意的重要行是格式說明符。

' A6 '字符指定unpack()函數獲取數據的前6個字節並將其解釋為字符串。然後將檢索到的數據存儲在一個關聯數組中,該數組的鍵名為“version”。

下面給出了另一個例子。

這將返回GIF文件的一些附加頭數據,包括圖像的寬度和高度。

<code>function get_gif_header($image_file)
{

/* Open the image file in binary mode */
if(!$fp = fopen ($image_file, 'rb')) return 0;

/* Read 20 bytes from the top of the file */
if(!$data = fread ($fp, 20)) return 0;

/* Create a format specifier */
$header_format =
'A6Version/' . # Get the first 6 bytes
'C2Width/' . # Get the next 2 bytes
'C2Height/' . # Get the next 2 bytes
'C1Flag/' . # Get the next 1 byte
'@11/' . # Jump to the 12th byte
'C1Aspect'; # Get the next 1 byte

/* Unpack the header data */
$header = unpack ($header_format, $data);


$ver = $header['Version'];

if($ver == 'GIF87a' || $ver == 'GIF89a') {
return $header;
} else {
return 0;
}
}

/* Run our example */
print_r(get_gif_header("aboutus.gif"));/<code>

上面的示例運行後打印以下內容。

<code>Array
(
[Version] => GIF89a
[Width1] => 97
[Width2] => 0
[Height1] => 33
[Height2] => 0
[Flag] => 247
[Aspect] => 0
)/<code>

下面我們將詳細討論格式說明符的工作方式。我將分解格式,給出每個字符的詳細信息。

<code>$header_format = 'A6Version/C2Width/C2Height/C1Flag/@11/C1Aspect';/<code>
高效還是炫技?PHP解析二進制文件,就靠這倆祖傳的函數,我——

更多格式選項可以在上圖找到。

寫在最後

我們展示的只是一個小小的例子,按照上圖所揭示的規律,您可以任意組裝成強大的解析函數。


分享到:


相關文章: