我在尝试在 foreach
循环中使用 array_combine
时遇到了麻烦。最终会出现错误:
PHP Fatal error: Allowed memory size of 268435456 bytes exhausted (tried to allocate 85 bytes) in
这是我的代码:
$data = array();
$csvData = $this->getData($file);
if ($columnNames) {
$columns = array_shift($csvData);
foreach ($csvData as $keyIndex => $rowData) {
$data[$keyIndex] = array_combine($columns, array_values($rowData));
}
}
return $data;
我使用的源文件 CSV 大约有 1,000,000 行。这一行
$csvData = $this->getData($file)
我使用 while 循环来读取 CSV 并将其分配到一个数组中,它工作正常,没有任何问题。问题来自于 array_combine 和 foreach 循环。
您有什么想法来解决这个问题或者只是有更好的解决方案吗?
已更新
这是读取 CSV 文件的代码(使用 while 循环)
$data = array();
if (!file_exists($file)) {
throw new Exception('File "' . $file . '" do not exists');
}
$fh = fopen($file, 'r');
while ($rowData = fgetcsv($fh, $this->_lineLength, $this->_delimiter, $this->_enclosure)) {
$data[] = $rowData;
}
fclose($fh);
return $data;
更新2
如果您正在使用 <=20,000~30,000 行的 CSV 文件,上面的代码可以正常工作。从 50,000 行及以上,内存将耗尽。
最佳答案
实际上,您在内存中保留(或试图保留)整个数据集的两个不同副本。首先,使用 getData()
将整个 CSV 日期加载到内存中,然后通过循环内存中的数据并创建一个新数组,将数据复制到 $data
数组中.
加载 CSV 数据时,您应该使用基于流的读取,以便在内存中仅保留一组数据。如果您使用的是 PHP 5.5+(顺便说一下,您绝对应该这样做),这很简单,只需将您的 getData
方法更改为如下所示:
protected function getData($file) {
if (!file_exists($file)) {
throw new Exception('File "' . $file . '" do not exists');
}
$fh = fopen($file, 'r');
while ($rowData = fgetcsv($fh, $this->_lineLength, $this->_delimiter, $this->_enclosure)) {
yield $rowData;
}
fclose($fh);
}
这利用了所谓的 generator这是 PHP >= 5.5 的功能。其余代码应该继续工作,因为 getData
的内部工作对于调用代码应该是透明的(只说了一半)。
更新解释现在如何提取列标题。
$data = array();
$csvData = $this->getData($file);
if ($columnNames) { // don't know what this one does exactly
$columns = null;
foreach ($csvData as $keyIndex => $rowData) {
if ($keyIndex === 0) {
$columns = $rowData;
} else {
$data[$keyIndex/* -1 if you need 0-index */] = array_combine(
$columns,
array_values($rowData)
);
}
}
}
return $data;
关于在 foreach 循环中使用 array_combine 时 PHP 内存耗尽,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/37341575/