【发布时间】: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);
}
算法的作用很简单:
- 读取 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