使用fgetcsv
,我能否以某种方式执行破坏性读取,其中我已阅读和处理的行将被丢弃,因此如果我没有在第一个文件中通过整个文件通过,我可以回来然后在the script timed out
之前离开的地方接听?
其他详细信息:
我从供应商处获得每日产品Feed,其中包含200mb .gz文件。当我解压缩文件时,它变成1.5gb .csv,有近500,000行和20-25个字段。我需要将这些信息读入MySQL数据库,最好是使用PHP,这样我就可以安排CRON每天在我的网络托管服务提供商处运行脚本。
主机提供商将服务器上的硬超时设置为180秒,任何单个脚本的最大内存利用率限制为128mb。我无法改变这些限制。
我的想法是使用fgetcsv函数从.csv中获取信息,但由于3分钟超时,我希望不得不对文件进行多次传递,我认为这样做会很好在我处理它的文件中,所以我不需要花费周期跳过已经在前一遍中处理过的行。
答案 0 :(得分:14)
从您的问题描述中,您确实需要切换主机。处理具有硬时间限制的2 GB文件不是一个非常有建设性的环境。话虽如此,从文件中删除读取行甚至不那么有建设性,因为你必须将整个2 GB重写为磁盘减去你已读过的部分,这非常昂贵。
假设您保存已处理的行数,可以跳过如下行:
$alreadyProcessed = 42; // for example
$i = 0;
while ($row = fgetcsv($fileHandle)) {
if ($i++ < $alreadyProcessed) {
continue;
}
...
}
但是,这意味着您每次浏览时都会从头开始阅读整个2 GB文件,这本身已经需要一段时间,您可以处理越来越少的行再次开始的时候。
这里最好的解决方案是记住文件指针的当前位置,ftell
是您正在寻找的功能:
$lastPosition = file_get_contents('last_position.txt');
$fh = fopen('my.csv', 'r');
fseek($fh, $lastPosition);
while ($row = fgetcsv($fh)) {
...
file_put_contents('last_position.txt', ftell($fh));
}
这使您可以直接跳回到您所在的最后位置并继续阅读。您显然希望在此处添加大量错误处理,因此无论您的脚本在哪个位置中断,您都不会处于不一致状态。
答案 1 :(得分:1)
当像Stream一样读取时,可以在一定程度上避免超时和内存错误。通过逐行读取,然后将每一行插入数据库(或相应地处理)。这样,每次迭代时只在内存中保留单行。请注意,不要尝试将巨大的csv文件加载到数组中,这会消耗大量内存。
if(($handle = fopen("yourHugeCSV.csv", 'r')) !== false)
{
// Get the first row (Header)
$header = fgetcsv($handle);
// loop through the file line-by-line
while(($data = fgetcsv($handle)) !== false)
{
// Process Your Data
unset($data);
}
fclose($handle);
}
答案 2 :(得分:0)
我认为一个更好的解决方案(连续倒回并写入打开文件流,效率很低)将跟踪每个记录读取的文件位置(使用ftell)并将其与数据存储在一起已经阅读 - 然后如果你必须恢复,那么只需要到最后一个位置。
您可以尝试使用mysql的读取文件函数直接加载文件(这可能会快得多)虽然我过去遇到过这个问题并最终编写了我自己的php代码。
主机提供商将服务器上的硬超时设置为180秒,任何单个脚本的最大内存利用率限制为128mb。我无法改变这些限制。
你有什么尝试?
内存可以通过php.ini文件之外的其他方式进行限制,但我无法想象任何人实际上可以阻止你使用不同的执行时间(即使禁用ini_set,也可以从命令行运行) php -d max_execution_time = 3000 /your/script.php或php -c / path / to / custom / inifile /your/script.php)
除非您尝试将整个数据文件放入内存中,否则应该没有内存限制为128Mb的问题