PHP-память исчерпана при использовании array_combine в цикле foreach
У меня проблемы при попытке использовать array_combine
в foreach
петля. Это приведет к ошибке:
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, который я использовал, имеет около 1000000 строк. Этот ряд
$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
Приведенный выше код работает без каких-либо проблем, если вы играете с CSV-файлом <=20000~30000 строк. Начиная с 50000 строк, память будет исчерпана.
1 ответ
На самом деле вы сохраняете (или пытаетесь сохранить) две разные копии всего набора данных в своей памяти. Сначала вы загружаете всю дату CSV в память, используя getData()
и вы копируете данные в $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);
}
Это использует так называемый генератор, который является функцией 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;