首页 文章

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

提问于
浏览
1

使用 fgetcsv ,我能以某种方式读取 a destructive 在第一遍中读取整个文件的行吗?我可以回来拿起我在 the script timed out 之前离开的地方?

Additional Details:

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

服务器上的硬超时由主机提供商设置为180秒,并且任何单个脚本的最大内存利用率限制为128mb . 这些限制不能由我改变 .

我的想法是使用fgetcsv函数从.csv中获取信息,但由于3分钟的超时,我希望不得不在文件中多次传递,我当时认为删除文件会很好因为我处理它所以我不需要花费周期跳过已经在前一遍中处理过的行 .

3 回答

  • 0

    从您的问题描述中,您确实需要切换主机 . 处理具有硬时间限制的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));
    }
    

    这允许您直接跳回到您所在的最后位置并继续阅读 . 您显然希望在此处添加大量错误处理,因此无论您的脚本在哪个位置中断,您都不会处于不一致状态 .

  • 13

    在像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);
    }
    
  • 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应该没有问题

相关问题