我想使用PHP在二进制文件中找到特定的字节序列。我用十六进制表示这个序列,以避免输入太多的0和1。要查找的序列是0x4749524f
。这是我现在想到的工作解决方案:
$mysequence = "4749524f";
$f = fopen($filename, "r") or die("Unable to open file!");
while(!feof($f)) {
$seq = fread($f, 4);
if(bin2hex($seq) == $mysequence) {
echo "found!";
break;
}
else if(!feof($f)) fseek($f, -3, SEEK_CUR);
}
算法的作用很简单:
- 读取4字节
- 检查它们是否等于序列
- 如果它们相等->找到!停止执行
- 如果它们不相等,并且我不在文件的末尾,返回3个字节到文件中并重复步骤1。
为什么要返回3个字节?因为如果这是文件的内容:
0000 4749 524f 0000 01b0 0013
如果我不返回3个字节,我将在第一次迭代中读取0000 4749
,在第二次迭代中读取524f 0000
,在第三次迭代中读取01b0 0013
,正如您所看到的,我错过了序列。
问题:它慢得要命…该应用程序必须处理最大50MB的文件,因此将花费很长时间才能找到此序列。
在PHP中是否有一个优化的函数可以完成这项工作?有没有一种更快的方法(不像我的那样愚蠢)来做到这一点?
首先,您的$mysequence
在搜索时不会改变,因此您可以调用hex2bin($mysequence)
一次并直接将其与$seq
进行比较。
为了更快,你可以尝试在大缓冲区中读取和搜索字符串。更大的缓冲区=>更快的搜索,但需要更多的内存。快速代码草案,这应该是什么样子的:
$mysequence = "4749524f";
$searchBytes = hex2bin($mysequence);
$crossing = 1 - length($searchBytes); // - (length - 1); see below
$buf = ''; $buflen = 10000;
$f = fopen($filename, "r") or die("Unable to open file!");
while(!feof($f))
{
$seq .= fread($f, $buflen);
if(strpos($seq, $searchBytes) === false) // strict comparation here. zero can be returned!
{
// keep last n-1 bytes, because they can be beginning of required sequence
$seq = substr($seq, $crossing);
}
else
{
echo "found!";
break;
}
}
unset($seq); // no need to keep this in memory any more
从磁盘读取总是需要很长时间。你不能指望磁盘缓存。这是操作系统的问题。相反,可以自己进行"缓存"。读入一长串字节,比如1M(或更多)。这减少了磁盘读取。然后在内存中搜索。在读取下一个1mb字节时,请确保将前一组的最后3个字节添加到它前面。搜索每个集合,直到找到为止。读取的实际大小需要在RAM使用和磁盘读取之间取得平衡。