Наиболее эффективный с точки зрения памяти способ разделения блоков переменного размера?


Есть ли способ сделать что-то вроде fread, но с переменной? То есть я хочу "читать" другую переменную в памяти по 1 МБ за раз.

Таким образом, у меня могло бы быть что-то вроде этого:

$data = ... ; // 10MB of data

$handle = fopen($data, "rb"); // Need something instead of fopen here

while (!feof($handle))
{
    $chunk = fread($handle, 1048576); // Want to read 1MB at a time

    doSomethingWithChunk($chunk);
}

fclose($handle);

У меня есть большой двоичный файл, загруженный в память, около 10 МБ. Я бы хотел разделить его на массив из блоков размером 1 МБ. Мне не нужны все куски размером 1 МБ в памяти сразу, поэтому я думаю, что мог бы сделать что-то подобное выше более эффективно, чем просто использовать встроенный PHP str_split функция.

Author: Rocket Hazmat, 2014-01-14

2 answers

Невозможно последовательно "прочитать" строку, которая уже загружена в память; на самом деле не более эффективно разделять ее. Накладные расходы нескольких переменных также потребляют больше памяти, чем одна. В идеале вы бы загрузили строку в поток, но в PHP на самом деле нет потока строк.

Если вы просто хотите работать со строкой по частям, вы можете просто перебирать ее подстроки:

$data;
$pointer = 0, $size = strlen($data);

$chunkSize = 1048576;
while ($pointer < $size)
{
    $chunk = substr($data, $pointer, $chunkSize);
    doSomethingWithChunk($chunk);
    $pointer += $chunkSize;
}

Я не уверен, как PHP обрабатывает большие строки внутренне, но в соответствии со строковой документацией , строка может быть только "размером до 2 ГБ (максимум 2147483647 байт)". Если ваш файл размером около 10 МБ, это не должно быть проблемой для PHP.

Другой вариант (возможно, лучший вариант) - загрузить $data в память или временный поток . Если вы хотите избавить среду от избыточной памяти, вы можете использовать оболочку потока php://temp, где некоторые данные хранятся во временном файле, если он превышает 2 МБ. Просто загрузите строку в поток как можно скорее, чтобы сэкономить память, а затем вы можете использовать функции файлового потока для нее.

$dataStream = fopen("php://temp", "w+b");
fwrite($dataStream, funcThatGetsData()); // try not to put data into a variable to save memory

while (!feof($dataStream))
{
    $chunk = fread($dataStream, 1048576); // want to read 1MB at a time
    doSomethingWithChunk($chunk);
}

fclose($dataStream);

Если вы получаете $data из другой функции, вы можете вместо этого передать $dataStream. Если вам необходимо заранее ввести $data в строку, обязательно вызовите unset(), чтобы освободить память:

$data = getData(); // string from some other function
$dataStream = fopen("php://temp", "w+b");
fwrite($dataStream, $data);
unset($data); // free 10MB of memory!
...

Если вы хотите сохранить все это в памяти, вы можете использовать php://memory, но в этом случае вы можете просто использовать строку.

 1
Author: coderstephen, 2014-01-14 19:35:12

Вы можете использовать как;

$handle = @fopen("path_to_your_file", "r");
if ($handle) {
    while (($buffer = fgets($handle, 1024)) !== false) {
        doSomethingWithChunk($buffer );
    }
    fclose($handle);
}
 1
Author: Hüseyin BABAL, 2014-01-14 18:29:53