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


Get random item from huge array or random file from subfolders, while staying within the memory limit

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

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

$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 内存不足?或者有没有更好的方法可以从如此庞大的数组中抓取随机物品?(或者甚至可以直接从所有子文件夹中获取一个随机文件?

不想覆盖内存限制!

文件数量不断变化。

好的,所以我现在正在做的 - 它有效 - 不是将迭代器转换为数组,而是计算迭代器中的项目,计算一个随机数,然后循环迭代器,直到我到达具有该数字的项目:

$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循环也需要几秒钟,所以如果我可以直接按编号从迭代器中检索元素,我会很高兴,但我找不到任何关于如何做到这一点的例子。

所以,如果你对如何解决1或2有想法,我将不胜感激。

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

但是,如果您的数组尺寸现在已知并根据输入(例如,文件树中的不同位置)而变化,那么事情就会变得更加复杂。幸运的是,有人在 10 年前回答了这个问题,并在 PHP 手册的评论中发布了一段截取的代码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;
}
?>

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