【问题标题】:Searching a sequence of bytes in a binary file in PHP?在 PHP 中搜索二进制文件中的字节序列?
【发布时间】:2015-09-09 18:44:17
【问题描述】:

我想使用 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. 如果它们不等于且 i 不在文件末尾,则返回文件 3 字节并重复步骤 1。

为什么我要返回 3 个字节?因为如果这是文件的内容:

0000 4749 524f 0000 01b0 0013

如果我不返回 3 个字节,我将在第一次迭代时读取 0000 4749,在第二次迭代时读取 524f 0000,在第三次迭代时读取 01b0 0013,如您所见,我错过了序列。

问题:速度太慢了...应用程序必须处理高达 50MB 的文件,因此要花很长时间才能找到这个序列。

PHP 中是否有一个优化的函数可以完成这项工作?有没有更快(不像我那样笨)的方法来做到这一点?

【问题讨论】:

  • 读入一长串字节,大概是 1M(或更多)。然后在内存中搜索。在读取接下来的 1Mbytes 时,一定要检查第一组的最后 3 个是否是针头的开始。
  • 好的,我要试试!谢谢。顺便说一句,我认为文件在读取过程中被缓存在内存中......你的意思是每次我运行 fread 函数时,文件都是直接从硬盘读取的?
  • @AlbertoFontana 这只是对相同方法的修改,仅读取较大的块(我认为 4-8k),然后是“在块中查找”(与“块精确匹配”相比)。要轻松处理拆分块,一种简单的方法是回溯,以便块实际上重叠了几个字节(这种近距离搜索在相对不频繁的情况下非常好)。系统调用次数的减少才是最大的性能差异。通过将 $mysequence 转换为字节序列,而不是总是转换读取的数据,还可以减少更多的工作。
  • 缓存是在操作系统的支配下完成的。对大量读取进行自己的缓存。
  • 读入下一组字节后,前置上一组字节的最后3个字节并开始搜索。

标签: php search optimization


【解决方案1】:

首先,您的$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

【讨论】:

  • 当你说:“你的 $seq 在搜索时没有改变,所以你可以调用一次 bin2hex() 时,我不明白。”。当然 $seq 会发生变化,因为我在每个循环中都读取了一个新序列……我错了吗?
  • 我的错。您可以致电hex2bin($mysequence) 并与$seq 进行比较。无需每次都调用bin2hex
【解决方案2】:

从磁盘读取总是需要很长时间。你不能指望磁盘缓存。那是操作系统的事情。相反,按原样做自己的“缓存”。读入一长串字节,可能是 1M(或更多)。这减少了磁盘读取。然后在内存中搜索。读取下一个 1Mbytes 时,请务必在其前面添加上一组的最后 3 个字节。搜索每组直到找到。读取的实际大小需要在 RAM 使用和磁盘读取之间取得平衡。

【讨论】:

    猜你喜欢
    • 2010-12-03
    • 2018-08-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-11-02
    • 1970-01-01
    • 1970-01-01
    • 2017-06-13
    相关资源
    最近更新 更多