我需要解析一些 CSV 数据并将其存储到数据库中。输入文件大约有 15000 行。它是用 Laravel 框架编写的。对数据库的请求花费了大约 0.2 秒,因此问题似乎出在 CSV 解析器中。有人可以告诉我如何在 PHP 中优化这段代码吗?代码如下所示:
protected function csv2array(string $csv)
{
try {
$return = [
'headers' => [],
'rows' => [],
];
$rows = explode(PHP_EOL, $csv);
$headers = str_getcsv(strtolower(array_shift($rows))); // Headers + strtolower()
$return['headers'] = $headers;
foreach ($rows as $row) {
$items = str_getcsv($row);
if ( count($items) !== count($headers) ) continue;
$items[2] = new Carbon($items[2]); // Third item is UTC datetime
$items[3] = new Carbon($items[3]); // Fourth item is UTC datetime
$items = array_combine($headers, $items);
$return['rows'][] = $items;
}
return $return;
} catch (Exception $e) {
Log::error($e->getMessage());
throw $e;
}
}
调用 csv2array() 的父代码如下所示
$csv = $request->getContent();
$csv = trim($csv);
$csvArray = $this->csv2array($csv);
$insertArray = $this->addDeviceIdToArray($csvArray['rows'], $device); // This is fast 0.2s
我的猜测是,您当前的设置会一次性加载所有内容。您的行数太多,这不是一条有效的路线。这是“简单快速”的解决方案,但有局限性。
IMO 最可能的问题是数据量。您将 15000 行加载到
$csv
中,然后将其复制到一个巨大的数组中。 PHP 确实不太擅长处理这样的信息。
我的建议是使用
fgetcsv
:
file_put_contents("test.csv", $request->getContent()); // write to file
protected function csvToArray(){
$handle = fopen("test.csv", "r");
$i = 0;
while (($items = fgetcsv($handle, null, ",")) !== false) {
$i++;
if($i === 1){
$headers = $items;
continue;
}
$items[2] = new Carbon($items[2]); // Third item is UTC datetime
$items[3] = new Carbon($items[3]); // Fourth item is UTC datetime
$items = array_combine($headers, $items);
$return['rows'][] = $items;
}
return $return;
}
file_put_contents("test.csv", $request->getContent()); // write to file
$csvArray = $this->csvToArray();
这样您就可以从内存密集型文件中进行流式传输,并且不会随文件大小而缩放。
(请注意,这是演示代码,还有改进的空间)