2015-09-09 3 views
5

Я хочу найти определенную последовательность байтов в двоичном файле с помощью PHP. Я представлял эту последовательность в шестнадцатеричной форме, чтобы не набирать слишком много 0 и 1. Последовательность поиска - 0x4749524f. Это рабочий раствор я придумал сейчас:Поиск последовательности байтов в двоичном файле в PHP?

$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 на третий и, как вы можете видеть я пропустил последовательность.

Проблема: медленно, как ад ... Приложению придется работать с файлами размером до 50 МБ, поэтому на этой последовательности понадобится навсегда.

Есть ли оптимизированная функция в PHP, которая бы выполняла эту работу? Есть ли более быстрый (не тупой, как мой) способ сделать это?

+1

Читайте в длинном наборе байтов, что-то вроде 1M (или больше). Затем выполните поиск в памяти. При чтении следующих 1 Мбайт обязательно проверьте, были ли последние 3 первого набора началом иглы. –

+0

Хорошо, я попробую! Благодарю. BTW, я думал, что файл был кэширован в memry во время чтения ... вы имеете в виду, что каждый раз, когда я запускаю функцию fread, файл читается непосредственно с жесткого диска? –

+0

@AlbertoFontana Это просто модификация того же подхода, только чтение в больших кусках (я бы сказал, 4-8k), а затем «найти в куске» (vs «chunk exact match»). Чтобы легко справиться с расщепленными кусками, простым способом является поиск назад, так что куски на самом деле перекрываются несколькими байтами (этот закрытый поиск достаточно хорош, если делать относительно нечасто). Сокращение числа системных вызовов - это то, что будет самой большой разницей в производительности. Кроме того, немного больше работы может быть уменьшено путем преобразования $ mysequence в последовательность байтов, вместо того чтобы всегда преобразовывать прочитанные данные. – user2864740

ответ

1

Выполнение чтения с диска всегда занимает много времени. Вы не можете рассчитывать на кеширование диска. Это ОС. Вместо этого сделайте свое «кэширование» как бы. Читайте в длинном наборе байтов, что-то вроде 1M (или больше). Это уменьшает чтение дисков. Затем выполните поиск в памяти. При чтении следующих 1 Мбайт обязательно добавьте к нему последние 3 байта предыдущего набора. Поиск каждого набора до тех пор, пока не будет найден. Фактический размер вашего считывания должен быть балансом между использованием ОЗУ и чтением диска.

3

Прежде всего, ваш $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 
+0

Я не понял, когда вы сказали: «Ваш $ seq не меняется во время поиска, поэтому вы можете вызвать bin2hex() один раз». Конечно, $ seq меняется, потому что я читаю новую последовательность в каждом цикле ... я ошибаюсь? –

+0

Моя вина. Вы можете вызвать '' 'hex2bin ($ mysequence)' '' и сравнить с $ seq. Без вызова '' 'bin2hex''' каждый раз. –

Смежные вопросы