在PHP中,有没有一种方法可以在循环中生成新的数组变量?

3
我正在寻找一种在循环中创建新数组的方法。不是值,而是数组变量。到目前为止,看起来这是不可能或复杂的,或者我还没有找到正确的方法。
例如,我有一个动态数量的值需要附加到数组中。
假设它将是200,000个值。出于服务器内存原因,我不能将所有这些值分配给一个数组,可以跳过此部分。
我可以为每个数组分配最多50,000个值。
这意味着,我需要创建4个数组以适应不同数组中的所有值。但下一次,我将不知道需要处理多少个值。
是否有一种方法可以根据每个数组的固定容量和值的数量生成所需数量的数组?
或者必须手动声明数组,没有变通方法?

我的目标是这样的:
$required_number_of_arrays = ceil(count($data)/50000);

for ($i = 1;$i <= $required_number_of_arrays;$i++) {

 $new_array$i = array();

 foreach ($data as $val) {
  $new_array$i[] = $val;
 }

}

// Created arrays: $new_array1, $new_array2, $new_array3

2
@encrypted21 你可以使用生成器从数据库中检索大量数据,这里有一个使用PDO的示例 - Xorifelse
1
生成器的概念是,在产生下一个值时,前一个值会从内存中删除。这样,您可以轻松地读取一个10GB的文本文件,并仅保留该文本文件每行所需的内存,这很少会超出最大允许的内存。 - Xorifelse
1
@encrypted21 请记住,生成器产生的数据需要立即处理。将该数据存储在另一个数组中将抵消它所产生的效果。 - Xorifelse
2
如果您由于内存问题而无法构建XML,您可以使用单个文档作为构建XML的“草稿”并执行$doc->saveXML($node)来获取该内部XML字符串,并使用fwrite附加将这些条目添加到输出文件中。完成后手动关闭外部xml元素。 - Scuzzy
显示剩余16条评论
6个回答

1
根据您的评论,似乎您不需要单独的数组变量。您可以重复使用同一个数组。当它达到最大大小时,请进行处理并重新初始化:
$max_array_size = 50000;

$n = 1;
$new_array = [];

foreach ($data as $val) {
    $new_array[] = $val;

    if ($max_array_size == $n++) {
        // process $new_array however you need to, then empty it
        $new_array = [];
        $n = 1;
    }
}
if ($new_array) {
    // process the remainder if the  last bit is less than max size
}

我的想法是生成一个包含第一批值的数组。然后,在循环中处理它,摆脱那个变量以释放内存。然后,让循环重复处理剩余的数组。由于建议使用生成器,我将尝试它们,因为它们确实解决了内存问题。 - encrypted21
哦,如果是这样的话,你就不需要单独的数组变量了。你可以重复使用同一个数组。 - Don't Panic
谢谢,我会尝试这种方法并决定哪种对我的代码最有效 :) - encrypted21

1
一种可能的方法是扩展ArrayObject。您可以内置限制分配多少个值,这意味着您需要构建一个类而不是$new_array$i = array(); 然而,最好研究生成器,但是Scuzzy已经想到了这个点子。
生成器的概念是,每次yield时,前面的引用就无法访问,除非您再次循环。它将被覆盖,而不像数组那样,您可以始终使用$data[4]遍历以前的索引。
这意味着您需要直接处理数据。将产生的数据存储到新数组中将抵消其效果。
使用生成器获取大量数据没有问题,但在使用之前应该了解它们的概念。

嗯,也许你是对的。正如@Scuzzy所说,实际上可以在不使用常规数组的情况下解决问题。问题在于这些数据导致单个数组超出内存限制。我可以通过ini_set来覆盖它,但这不是一个解决方案,因为它只会严重滥用服务器。我想处理每个数组,销毁它,然后循环会给出剩余的数组。但生成器听起来像是真正的解决方案。 - encrypted21
让我知道它的运作情况,我通常会尽可能高效地编写脚本\程序,从未遇到过需要解决的这些问题,所以我也很好奇。但我怀疑它显然会解决这种情况。 - Xorifelse
当然,我会告诉你的,可能是明天我再编码的时候 :) - encrypted21
1
我几天前才能够适当地测试所有内容,所以现在报告。生成器已经正常工作 :) - encrypted21

0
你可以创建一个数组并使用extract()从该数组中获取变量:
$required_number_of_arrays = ceil($data/50000);
$new_arrays = array();
for ($i = 1;$i <= $required_number_of_arrays;$i++) {
   $new_arrays["new_array$i"] = $data;
}
extract($new_arrays);

print_r($new_array1);
print_r($new_array2);
//...

我猜这会在PHP的变量表中留下巨大的内存印记。不仅如此,所有的值仍然停留在内存中。并没有解决问题,只会让情况变得更糟。但再次强调,这只是我的逻辑推测。 - Xorifelse

0

我认为在你的情况下,你需要创建一个包含所有生成的数组的数组。

因此,在循环之前首先声明一个变量。

$global_array = [];

在循环中,您可以生成名称并填充该数组。
$global_array["new_array$i"] = $val;

循环结束后,您可以使用该数组进行操作。但我认为最终这不会解决您的内存限制问题。如果用200k条目填充5个数组,那么与填充一个200k数组相同,数据量是相同的。因此,您可能会以两种方式超过内存限制。如果您无法定义限制,则可能会出现问题。

ini_set('memory_limit', '-1');

因此,您只能在直接处理值而不保存数组中的内容时防止该问题。例如,如果运行数据库查询并直接处理值并仅保存结果。

您可以尝试类似以下的方法:

foreach ($data as $key => $val) {
   $new_array$i[] = $val;
   unset($data[$key]);
}

然后你的值存储在新数组中,你删除原始数据数组的值。在50k之后,你必须创建一个新的数组。

更简便的方法是使用array_chunk将你的数组分成几部分。

https://secure.php.net/manual/en/function.array-chunk.php


谢谢您的回答,但是这正是问题所在,它无法解决问题。我想做的是创建较小的数组,处理一个,销毁它,然后让循环生成其他的数组等等。覆盖内存限制是个坏主意,因为它只会过载服务器的性能。 - encrypted21
然后遍历您的$data数组,将值保存在新数组中并弹出主数组中的该值。我更新了我的答案。 - René Höhle
它不会只是将超过数据的所有内存移动到新数组中吗?我可以将50000个值移动到新数组中,但实现起来太复杂了。我认为生成器是一个很好的想法。它们处理每个值,然后销毁旧值,只保留一个值在内存中。 - encrypted21

0

没有必要使用多个变量。如果你想将数据分块处理,以避免占用过多内存,可以重复使用同一个变量。当你重新赋值时,该变量的先前内容将被垃圾回收。

$chunk_size = 50000;
$number_of_chunks = ceil($data_size/$chunk_size);
for ($i = 0; $i < $data_size; $i += $chunk_size) {
    $new_array = array();
    foreach ($j = $i * $chunk_size; $j < min($j + chunk_size, $data_size); $j++) {
        $new_array[] = get_data_item($j);
    }
}

$new_array[$i] 的作用与您提出的 $new_array$i 相同。


谢谢,但这与将所有数据附加到一个单一数组中相同。通过实现可变变量,内存使用量不会缩小。 - encrypted21
1
这不是一个包含所有数据的单一数组。顶层数组与其元素所引用的数组是不同的。 - Barmar
我写错了 ceil($data/50000)。数据库中应该有一个数据计数变量。$data 是从数据库查询中获取的关联数组结果。你所建议的是变量变量,这只会改变值的存储方式。我的问题是创建不同的数组,而不是一个包含其他数组的数组。我需要处理每个数组,销毁它,然后生成和处理下一个数组以释放进程中的内存。我的想法被生成器打败了。 - encrypted21
1
如果你在处理完数组后要销毁它,为什么它们需要在不同的变量中?使用相同的变量并重新初始化它,这将释放先前数组的内存。 - Barmar
谢谢!我会仔细查看这些解决方案,并检查哪一个在我的情况下最有效 :) - encrypted21
显示剩余2条评论

0
你可以像这样做:
$required_number_of_arrays = ceil(count($data)/50000);
for ($i = 1;$i <= $required_number_of_arrays;$i++) {
 $array_name = "new_array_$i";
 $$array_name = [];
 foreach ($data as $val) {
  ${$array_name}[] = $val;
 }
}

我不太明白它是如何工作的,也不知道它确切的功能是什么 :D - encrypted21
这是一个“变量变量”。基本上,$$array_name获取$array_name的值并将其转换为变量。因此,在第一次传递中,$$array_name将等同于$new_array_1,在第二次传递中,它将等同于$new_array_2,依此类推。请参阅http://php.net/manual/en/language.variables.variable.php。 - José A. Zapata
我现在会继续使用生成器,并尝试在该过程中清空一个单独的数组,就像你建议的那样。对于我来说,你的建议看起来有点混乱,因为我不太熟悉使用双 $ 等。但无论如何,我也会尝试一下 :) 谢谢! - encrypted21
1
变量变量,在我看来是应该避免使用的。因为坦白说,你永远不知道哪个变量被定义了(除非读源代码),也没有任何IDE可以帮助你弄清楚这一点。它还允许出现语法错误,比如在PHP中是不允许使用$0的,除非使用这种方法进行定义。而且它也无法解决内存问题,因为每个值都会占用PHP的内存空间,不包括其他15万个值。 - Xorifelse

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接