从子文件夹中的大型数组或随机文件中获取随机项,同时保持在内存限制内

时间:2016-03-19 19:44:59

标签: php arrays random iterator

我正在尝试从文件夹中的所有子文件夹中获取随机文件。

首先,我使用以下代码获取所有文件的迭代器:

$path = "/path/to/folder";

$folder = new RecursiveDirectoryIterator($path);
$iterator = new RecursiveIteratorIterator($folder);

$files = new RegexIterator($iterator,
                           '/^.+\.(jpg|jpeg|png|gif)$/i',
                           RecursiveRegexIterator::GET_MATCH);

这似乎有效(并在一瞬间完成)。现在我想从生成的迭代器中获取一个随机项。我使用这段代码(这是第14行):

$image = array_keys(iterator_to_array($files))[mt_rand(0,
                                           count(iterator_to_array($files)) - 1)];

该文件夹包含334327 对象,并且在执行几秒钟后,iterator_to_array()因以下错误而死亡:

Fatal error: Allowed memory size of 134217728 bytes exhausted
             (tried to allocate 1232 bytes) in /script.php on line 14

如何更改代码以避免PHP耗尽内存?或者是否有更好的方法从这么庞大的数组中获取随机项? (或者甚至可以直接从所有子文件夹中获取随机文件?)

想要override the memory limit

文件数量不断变化。

3 个答案:

答案 0 :(得分:1)

好吧,所以我现在正在做的 - 它的工作原理 - 不是将迭代器转换为数组,而是计算迭代器中的项目,计算一个随机数,然后遍历迭代器直到我到达带有该号码的项目:

$path = "/path/to/folder";

$folder = new RecursiveDirectoryIterator($path, FilesystemIterator::SKIP_DOTS);
$iterator = new RecursiveIteratorIterator($folder);

$files = new RegexIterator($iterator, '/^.+\.(jpg|jpeg|png|gif)$/i', RecursiveRegexIterator::GET_MATCH);

$i = mt_rand(0, iterator_count($files) - 1);

$c = 0;
foreach($files as $file) {
    if ($i == $c) {
        $image = $file[0];
        break;
    }
    $c++;
}

我说过,现在有效,但是:

  1. 计数需要大约10秒,所以我很乐意以某种方式缩写;以及

  2. foreach-loop也需要几秒钟,所以如果我能用数字直接从迭代器中检索一个元素,我会很高兴,但是我找不到任何关于如何这样做。

  3. 所以,如果您对如何解决1或2有所了解,我将不胜感激。

答案 1 :(得分:0)

听起来你正在寻找一个从数组中选取一个随机元素的函数。如果数组维度已知/常量,则可以迭代PHPs array_rand() function

但是,如果您的数组维度现在已知并且根据输入(,例如,文件树中的其他位置)而更改,则事情会变得更复杂。幸运的是,10年前有人回答了这个问题,并在PHP manual for array_rand():

的评论中发布了一段代码。
<?php
/**
* Returns a number of random elements from an array.
*
* It returns the number (specified in $limit) of elements from
* $array. The elements are returned in a random order, exactly
* as it was passed to the function. (So, it's safe for multi-
* dimensional arrays, aswell as array's where you need to keep
* the keys)
*
* @author Brendan Caffrey  <bjcffnet at gmail dot com>
* @param  array  $array  The array to return the elements from
* @param  int    $limit  The number of elements to return from
*                            the array
* @return array  The randomized array
*/
function array_rand_keys($array, $limit = 1) {
    $count = @count($array)-1;

    // Sanity checks
    if ($limit == 0 || !is_array($array) || $limit > $count) return array();
    if ($count == 1) return $array;

    // Loop through and get the random numbers
    for ($x = 0; $x < $limit; $x++) {
        $rand = rand(0, $count);

        // Can't have double randoms, right?
        while (isset($rands[$rand])) $rand = rand(0, $count);

        $rands[$rand] = $rand;
    }

    $return = array();
    $curr = current($rands);

    // I think it's better to return the elements in a random
    // order, which is why I'm not just using a foreach loop to
    // loop through the random numbers
    while (count($return) != $limit) {
        $cur = 0;

        foreach ($array as $key => $val) {
            if ($cur == $curr) {
                $return[$key] = $val;

                // Next...
                $curr = next($rands);
                continue 2;
            } else {
                $cur++;
            }
        }
    }

    return $return;
}
?>

答案 2 :(得分:0)

据我所知,您的脚本搜索所有子文件夹,如果总计数334327是文件+文件夹的数量,那么您需要将您的sript分开以获取随机路径然后获取路径中的随机文件。换句话说,您可以构建所有子文件夹和文件的图形,并在图形中选择随机点。