5

使用fgetcsv,我能否以某种方式进行破坏性读取,其中我已读取和处理的行将被丢弃,所以如果我没有在第一遍中通过整个文件,我可以回来并从我之前离开的地方继续the script timed out

额外细节:

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

我在服务器上设置了一个硬超时,由托管服务提供商设置为 180 秒,并且任何单个脚本的最大内存使用限制为 128mb。我无法更改这些限制。

我的想法是使用 fgetcsv 函数从 .csv 中获取信息,但由于 3 分钟超时,我预计必须在文件中多次传递,我认为削减文件会很好当我处理它时,我不需要花费周期跳过在前一次传递中已经处理过的行。

4

3 回答 3

18

根据您的问题描述,听起来您确实需要切换主机。处理具有硬时间限制的 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));
}

这使您可以直接跳回到上一个位置并继续阅读。您显然想在此处添加大量错误处理,因此无论您的脚本在哪一点被中断,您都不会处于不一致的状态。

于 2013-10-22T12:44:32.193 回答
5

You can avoid timeout and memory error to some extent when reading like a Stream. By Reading line by line and then inserts each line into a database (Or Process accordingly). In that way only single line is hold in memory on each iteration. Please note don't try to load a huge csv-file into an array, that really would consume a lot of memory.

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);
}
于 2013-10-22T11:35:41.947 回答
1

我认为更好的解决方案(连续倒带和写入打开的文件流会非常低效)是跟踪读取的每条记录的文件位置(使用ftell)并将其与您读取的数据一起存储 - 然后如果你必须恢复,然后才 fseek 到最后一个位置。

您可以尝试使用 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 的内存限制应该没有问题

于 2013-10-22T12:49:07.883 回答