PHP在一个巨大的csv文件上使用fgetcsv

PHP在一个巨大的csv文件上使用fgetcsv,php,fgetcsv,Php,Fgetcsv,使用fgetcsv,我是否可以以某种方式进行破坏性的读取,其中我已读取和处理的行将被丢弃,因此,如果我在第一次读取时没有完成整个文件,我可以返回并拾取脚本超时之前停止的位置 其他详细信息: 我从一个供应商那里得到了一个每日产品提要,它是一个200mb.gz文件。当我解包文件时,它会变成一个1.5gb.csv文件,有将近500000行和20-25个字段。我需要将这些信息读入MySQL数据库,最好是使用PHP,这样我就可以安排一个CRON每天在我的web托管提供商处运行脚本 托管提供商将服务器上的硬

使用
fgetcsv
,我是否可以以某种方式进行破坏性的读取,其中我已读取和处理的行将被丢弃,因此,如果我在第一次读取时没有完成整个文件,我可以返回并拾取脚本超时之前停止的位置

其他详细信息:

我从一个供应商那里得到了一个每日产品提要,它是一个200mb.gz文件。当我解包文件时,它会变成一个1.5gb.csv文件,有将近500000行和20-25个字段。我需要将这些信息读入MySQL数据库,最好是使用PHP,这样我就可以安排一个CRON每天在我的web托管提供商处运行脚本

托管提供商将服务器上的硬超时设置为180秒,任何单个脚本的最大内存使用限制为128mb。我不能改变这些限制


我的想法是使用fgetcsv函数从.csv中获取信息,但由于3分钟的超时,我希望必须多次传递该文件,我在想,在处理文件的过程中删减它会很好,这样我就不需要花时间跳过上一个过程中已经处理过的行。

当像流一样读取时,可以在一定程度上避免超时和内存错误。通过逐行读取,然后将每一行插入数据库(或相应的进程)。这样,在每次迭代中,内存中只保留一行。请注意,不要试图将一个巨大的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);
}

根据您的问题描述,听起来您确实需要切换主机。处理一个有严格时间限制的2GB文件不是一个很有建设性的环境。话虽如此,从文件中删除读取行更不具建设性,因为您必须将整个2GB重新写入磁盘,减去您已经读取的部分,这是非常昂贵的

假设您保存了已处理的行数,您可以像这样跳过行:

$alreadyProcessed = 42; // for example

$i = 0;
while ($row = fgetcsv($fileHandle)) {
    if ($i++ < $alreadyProcessed) {
        continue;
    }

    ...
}
这可以让你跳回上一个位置继续阅读。显然,您希望在此处添加大量错误处理,因此无论脚本在哪一点中断,您都不会处于不一致的状态。

我认为更好的解决方案(连续倒带并写入打开的文件流可能效率低下)是跟踪每次读取记录的文件位置(使用)并将其与您读取的数据一起存储-然后,如果您必须继续,则只需查看最后一个位置

您可以尝试直接使用mysql的read file函数加载文件(这可能会快得多),尽管我以前在这方面遇到过问题,最后编写了自己的php代码

托管提供商将服务器上的硬超时设置为180秒,任何单个脚本的最大内存使用限制为128mb。我不能改变这些限制

你试过什么

内存可以通过php.ini文件以外的其他方式进行限制,但我无法想象有人会阻止您使用不同的执行时间(即使禁用了ini_集,也可以从命令行运行php-d max_execution_time=3000/your/script.php或php-c/path/to/custom/inifile/your/script.php)


除非您试图将整个数据文件放入内存,否则128Mb的内存限制应该没有问题。请发布一些代码。否则,帮助您优化代码是不可能的。难道您不能只保存已解析的行数吗?一个“破坏性读取”将要求您重写整个文件,以获取所有要删除的内容。那太贵了,你不会想要的@moby04请原谅,我不熟悉fgetcsv函数,有没有办法在后续执行中跳过
x
行?@Robert82:鉴于此函数使用相同的文件处理程序运行,您可以轻松使用其他文件函数,如fseek…这与我的想法类似,但有3分钟的超时,我不希望一次就能看完整个文件。有没有办法在第二次传球时回来,然后“跳”到某一排?假设我第一次完成125000行,第二次可以从125001行开始吗?很好的解决方案,非常整洁优雅。帮我度过了这个难关。在一个黑客日里救了我。谢谢你。
$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));
}