Функция хэш-функции Opensubtitles не работает для больших файлов
Я использую функцию ниже для вычисления хэша opensubtitles.org для файлов фильмов. Он работает в основном, но с большими файлами я получаю следующую ошибку.
Я действительно не понимаю, потому что всегда должны быть доступные данные.
Может ли кто-нибудь указать мне в правильном направлении?
PHP Предупреждение: unpack(): Тип v: недостаточно ввода, нужно 2, 0 file.php в строке 169
function OpenSubtitlesHash($file)
{
$handle = fopen($file, "rb");
$fsize = filesize($file);
$hash = array(3 => 0,
2 => 0,
1 => ($fsize >> 16) & 0xFFFF,
0 => $fsize & 0xFFFF);
for ($i = 0; $i < 8192; $i++)
{
$tmp = ReadUINT64($handle);
$hash = AddUINT64($hash, $tmp);
}
$offset = $fsize - 65536;
fseek($handle, $offset > 0 ? $offset : 0, SEEK_SET);
for ($i = 0; $i < 8192; $i++)
{
$tmp = ReadUINT64($handle);
$hash = AddUINT64($hash, $tmp);
}
fclose($handle);
return UINT64FormatHex($hash);
}
function ReadUINT64($handle)
{
$u = unpack("va/vb/vc/vd", fread($handle, 8));
return array(0 => $u["a"], 1 => $u["b"], 2 => $u["c"], 3 => $u["d"]);
}
function AddUINT64($a, $b)
{
$o = array(0 => 0, 1 => 0, 2 => 0, 3 => 0);
$carry = 0;
for ($i = 0; $i < 4; $i++)
{
if (($a[$i] + $b[$i] + $carry) > 0xffff )
{
$o[$i] += ($a[$i] + $b[$i] + $carry) & 0xffff;
$carry = 1;
}
else
{
$o[$i] += ($a[$i] + $b[$i] + $carry);
$carry = 0;
}
}
return $o;
}
function UINT64FormatHex($n)
{
return sprintf("%04x%04x%04x%04x", $n[3], $n[2], $n[1], $n[0]);
}
Ответы
Ответ 1
Если вы предоставили некоторую дополнительную информацию: версию системы, версию php, размер больших файлов, тип файлов (простые файлы, URL-адреса и т.д.) - это даст больше информации для точного ответа.
Основное предположение, что вы находитесь на 32-битной системе и имеете проблемы с filsize
с файлами более 2 ГБ. Из документов:
Примечание. Поскольку тип целочисленного типа PHP подписан и многие платформы используют 32-битные целые числа, некоторые функции файловой системы могут возвращать неожиданные результаты для файлов размером более 2 ГБ.
Вероятно, вы получите неправильное значение filesize
и, следовательно, не можете точно прочитать байты с байтами. Этот комментарий объясняет, как получить размер больших файлов, а также отмечает, что fseek
использует int
внутренне, поэтому вы не можете поместить указатель после 2GB порог. Вам нужно будет fread
в эту позицию.
Можно проверить другую гипотезу:
-
fread
может читать больше данных, чем запрашивается при определенных циклах:
если поток считывается с буферизацией и не представляет собой обычный файл, производится не более одного считывания до нескольких байтов, равных размеру блока (обычно 8192); в зависимости от ранее буферизованных данных размер возвращаемых данных может быть больше размера блока.
-
stat
кеш не позволяет получить точное значение размера файла;
Ответ 2
Вы никогда не проверяете, имеет ли ваш дескриптор $какой-либо resoure, когда ваш дескриптор $имеет значение null или false, вы получите ту же ошибку
PHP Warning: unpack(): Type v: not enough input, need 2, have 0 in file.php on line 169
Итак, добавьте чек, прежде чем что-то сделать с помощью $handle
if(!is_null($handle)){
// Do something..
}
Ответ 3
Вам не нужно и не следует вычислять общий размер файла. Если размер файла превышает PHP_INT_MAX
, тогда результат будет неточным,
Лучшим решением является использование fseek()
в конце файла:
fseek($handle, -65536, SEEK_END);