在PHP二进制文件中搜索字节序列


Searching a sequence of bytes in a binary file in PHP?

我想使用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);
}

算法的作用很简单:

  1. 读取4字节
  2. 检查它们是否等于序列
  3. 如果它们相等->找到!停止执行
  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使用和磁盘读取之间取得平衡。