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;
Другие вопросы по тегам