我有一个我无法控制的相当大的 csv 文件(至少对于网络而言)。它有大约 100k 行,并且只会变得更大。
我正在使用 Drupal 模块提要根据这些数据创建节点,它们的解析器以 50 行为一组进行解析。但是,他们的解析器不能正确处理引号,并且无法解析大约 60% 的 csv 文件。fgetcsv 有效,但据我所知并没有批量处理。
在尝试使用 fgetcsv 读取整个文件时,PHP 最终耗尽了内存。因此,我希望能够将事情分解成更小的块。这可能吗?
我有一个我无法控制的相当大的 csv 文件(至少对于网络而言)。它有大约 100k 行,并且只会变得更大。
我正在使用 Drupal 模块提要根据这些数据创建节点,它们的解析器以 50 行为一组进行解析。但是,他们的解析器不能正确处理引号,并且无法解析大约 60% 的 csv 文件。fgetcsv 有效,但据我所知并没有批量处理。
在尝试使用 fgetcsv 读取整个文件时,PHP 最终耗尽了内存。因此,我希望能够将事情分解成更小的块。这可能吗?
fgetcsv()
通过从给定的文件指针一次读取一行来工作。如果 PHP 内存不足,也许您正试图一次解析整个文件,将其全部放入一个巨大的数组中。解决方案是逐行处理它而不将其存储在一个大数组中。
要更直接地回答批处理问题,请从文件中读取n行,然后使用ftell()
查找文件中结束的位置。记下这一点,然后您可以在将来的某个时间通过调用fseek()
before回到它fgetcsv()
。
好吧,创建一个函数来解析一堆行:
function parseLines(array $lines) {
foreach ($lines as $line) {
//insert line into new node
}
}
然后,只需批量处理:
$numberOfLinesToBatch = 50;
$f = fopen($file, 'r');
if (!$f) die('implement better error checking');
$buffer = array();
while ($row = fgetcsv($f)) {
$buffer[] = $row;
if (count($buffer) >= $numberOfLinesToBatch) {
parseLines($buffer);
$buffer = array();
}
}
if (!empty($buffer)) {
parseLines(buffer);
}
fclose($f);
它将数据流式传输,您可以通过调整变量来调整它缓冲的行数......
我怀疑问题在于您在内存中存储了太多信息,而不是您如何从磁盘读取 CSV 文件。(即: fgetcsv 一次只会读取一行,所以如果一行的数据导致你内存不足,你就有麻烦了。)
因此,您只需使用以下方法:
或者,您可以通过 PHP 的命令行版本执行 CSV 处理,并使用具有更大内存限制的自定义 php.ini。