PHP 操作二进制流,读取文件结构
本文包含的内容:详细的pack和unpack调用详解,16进制数字字符串保存到文件,读取文件返回值,等
本文包含的内容:详细的pack和unpack调用详解,16进制数字字符串保存到文件,读取文件返回值,等包含实例:读取
BMP图片信息函数:
//将16进制字符串转换为此值的字符串。
function hexstr_str( $hetstr)
//两个版本,一个是pack实现,一个是chr方法实现
曾经为了处理源文件就百度了下php 2 进制文件,结果出来的貌似都是一个人写的,别人复制和转载的,而且我也偏偏没有看懂。挺郁闷的。因此就有了下文,我就自己搞打,研究研究。Php处理
2进制无非就是使用pack和unpack,在后文讲讲其他的办法。
函数原型:
string pack ( string format [, [mixed](http://cn2.php.net/manual/en/language.pseudo-types.php#language.types.mixed) args [, mixed $... ]] )
array unpack ( string data )
Pack是讲所给的参数更具所给的格式打包成2进制字符串。手册上说这个函数和Perl的基本相同,只是在格式上去掉了s,u等。这里有一点要注意的是,有符号和无符号的数由pack转换出来的结果相同,但是会影响unpack的结果。
先看一个小小的实例:
$data = pack("N", 0x12345);
var_dump($data);
$fn = 'out.text';
fn,'w');
fwrite(data);
结果很明了,用法很简单。
关于N模式:
unsigned long (always 32 bit, big endian byte order)
无符号长整型,总是返回32位
格式:
mode | 说嘛 |
---|---|
a | 将字符串空白以 NULL 字符填满 |
A | 将字符串空白以 SPACE 字符 (空格) 填满 |
h | 十六进位字符串,低位在前 |
H | 十六进位字符串,高位在前 |
c | 有号字符 |
C | 无号字符 |
s | 有号短整数 (十六位,依计算机的位顺序) |
S | 无号短整数 (十六位,依计算机的位顺序) |
n | 无号短整数 (十六位, 高位在后的顺序) |
v | 无号短整数 (十六位, 低位在后的顺序) |
i | 有号整数 (依计算机的顺序及范围) |
I | 无号整数 (依计算机的顺序及范围) |
l | 有号长整数 (32位,依计算机的位顺序) |
L | 无号长整数 (32位,依计算机的位顺序) |
N | 无号短整数 (32位, 高位在后的顺序) |
V | 无号短整数 (32位, 低位在后的顺序) |
f | 单精确浮点数 (依计算机的范围) |
d | 倍精确浮点数 (依计算机的范围) |
x | 空位 |
X | 倒回一位 |
@ | 填入 NULL 字符到绝对位置 |
关于pack的调用方法。
因为pack是支持多个不定个数参数的,所以每个参数都要指定一个转换的模式。
$data = pack("Nn", 0x12345, 0x12345);
当你参数的个数多于格式的个数时,出现:
Warning: pack() [function.pack]: 1 arguments unused in E:\host\htdocs\Lab\Binary.php on line 4
Warning级错误。
这个还有个注意的是*模式。
$data = pack("Nn*", 0x12345, 0x12345, 0x12345);\最后两个是16位,所以输出里面是2字节
var_dump($data);
$fn = 'out.text';
fn,'w');
fwrite(data);
输出:
看出来了最后两个是一样的。因为手册上没说,但是我猜测*模式就是“同上”的意思吧。
基础知识:
1字节 = 8位 = 2^8 = 256
一个gb2312字符2字节,一个ascii字符1字节。
关于模式的选择都由个人使用的情况决定,这里我给出我自己写的一个函数。
//将16进制字符串转换为此值的字符串。
function hexstr_str( $hetstr)
{
$re = '';
for( hetstr[ i += 4)
//echo substr( i, 2);
hetstr, $i, 4)) * 1);
hetstr) - $i;
hetstr, hetstr) - $i);
len % 2) ? '0':'';
len < 3 ? 'v': 'n';
format, $hetstr * 1);
return $re;
}
测试:
$data = hexstr_str( 'ABCDc');
输出:
有点遗憾的是它都要用0来补满16位。不过总的来说还是很不错的啦。
Unpack的使用,我觉得用着这个感觉挺揪心的。
unpack() works slightly different from Perl as the 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 /.
Unpack的运行和Perl的有些微不同,在php中unpack的结果是由一个数组返回的。因此你需要接受这些数据就要给格式命名,以/分隔。
手册上的实例;
<?php
binarydata);
?>
看一个我个人的调用实例:
$data = pack("n", ('0x'.'abc' ) * 1);
$data = hexstr_str( 'ABCDc');
data;
s.$s);
输出结果:
array(3) {
["wen"]=>
int(-1412579328)
["stort1"]=>
int(43981)
["stort2"]=>
int(49152)
}
上式结果也就是
wen:abcdc000
stort1:abcd
stort2:c000
看着这样的结果和输入的格式我觉得很揪心的。不过这里还是有点好处的,这个我们可以格式化输出一个文件的头信息什么的。
详细讲讲uppack的格式和输入的关系。
"Nwen/n2stort"
即第一个解析以N为格式,以wen为名字。
第二个解析以n为格式解析两次,以short为名字。
生成的short的名字是以short1,short2,……shortn这样的形式增长的。
解析的方法:
N,先在输入(s)中读入32位长,即4字节。然后再用N转换,此时指向s指针的位置已经到了4了,第一个s的话会报错:
Warning: unpack() [function.unpack]: Type n: not enough input, need 2, have 0 in E:\host\htdocs\Lab\Binary.php on line 7
bool(false)
此时的返回值为false。
在解析完第一个N后遇到了“/”进入下一组的解析,解析的格式为n,有2组,名字为short
所以short1为输入的第4-6解析的结果,而short2为6-8的结果。都是一一对应的。
这样看来也不是很难嘛是吧。
个人方法:
Perl思想中有一条是:
There's More Than One Way To Do It.
我也觉得如此。
Php中有个函数叫做
String chr( int $ascii )
返回一个字节,以ascii值为参数。
原本的ascii只有127的,但是后来扩展到了255加上0那就是256 很好 这样就都能表示了。
把上面那个函数稍微改造一下就是:
//将16进制字符串转换为此值的字符串。
function hexstr_str( $hexstr)
{
$re = '';
for( hexstr[ i += 2)
hexstr[ hexstr[ $i + 1]) * 1);
if( strlen( hexstr) > i)
hexstr[ $i].'0') * 1);
return $re;
}
代码简洁了很多啊。看起来顺眼多了。
置于我为啥研究这个玩意儿,我最开始也就是为了用啦读取BMP源文件的。
这里我们来简单写个读取BMP头信息的函数。
注意,这里是以24/32位图举例,16/8/2位位图在这里可能行不通的,因为数据结构不同。
BMP文件结构:
Begin
//文件头
section "BMP File Header"
read-only char[2] "BMP_ID" // 00
uint32 "File size" // 02
uint32 "Reserved" // 06
uint32 "ImageDataOffset" // 0A
endsection
//信息头
section "BMP Info Header"
uint32 "HeaderSize" // 0E
uint32 "Width" // 12
uint32 "Height" // 16
uint16 "Planes" // 1A
uint16 "BPP" // 1C
uint32 "CompessionMethod" // 1E
uint32 "ImageSize" // 22
uint32 "XPixelsPerMeter" // 26
uint32 "YPixelsPerMeter" // 2A
uint32 "PaletteSize" // 2E
uint32 "ColorsImportant" // 32
endsection
//调色板信息
section "Palette(If PaletteSize=0 then no palette)"
numbering 0
{
byte "B[~]"
byte "G[~]"
byte "R[~]"
byte "A[~]"
} [PaletteSize]
endsection
end
实现的代码如下:
$data = file_get_contents( 'Wener.bmp');
data);
echo '<pre>';
var_dump( $array);
foreach( k => $v)
echo v),'<br>';
请更改为你自己的图片地址后测试。我的输入如下:
BMP_ID:16973
文件大小:307254
保留字:0
图片信息偏移:54
头大小:40
宽:240
高:320
Planes:1
BPP:32
压缩方法:0
图片大小:307200
横轴上每像素宽:0
竖轴上每像素宽:0
调色板大小:0
重要的颜色:0
我的测试文件:
总结:
总的,来说,php的这两个函数无疑是很强大的。用来分析文件也很好用。
或许不足的就是缺少这方面的案例吧,没见到很多人用过这个。
以后自己学习文件的结构也会常常用到这个函数,有时候觉得unpack比pack更有魅力。关于更详细的BMP文件结构和信息的获取我会另外写一篇文章的。这篇只是略微的涉及,作为一个例子而已。我还是觉得
Php处理2进制流的魅力是非常强大的。
置于建立在这个之上写更多的运用(加密,协议,破解,获取文件信息)什么的就看个人发挥了。