2013-10-22 116 views
1

使用fgetcsv使用fgetcsv,可我不知做破坏性读,我已经阅读并处理将被丢弃,所以如果我不第一遍撑过整个文件行,我可以回来接我在the script timed out之前离开的地方吗?PHP在一个巨大的CSV文件

其他细节:

我越来越从要像一个200MB的.gz文件供应商每天的产品饲料。当我解压文件时,它变成了一个1.5GB的.csv文件,有近50万行和20 - 25个字段。我需要将这些信息读入MySQL数据库,理想情况下使用PHP,以便我可以安排CRON每天在我的虚拟主机提供商处运行脚本。

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

我的想法是使用fgetcsv函数从.csv获取信息,但由于3分钟超时,我期望必须在该文件上进行多次传递,我认为这样可以很好地消除在我处理文件时,我不需要花费周期跳过已经在前一遍中处理过的行。

+0

请张贴一些代码。否则,协助您优化代码是不可能的。 –

+0

CSV文件的大小? –

+1

难道你不能保存你已经解析的行数吗? – Moby04

回答

12

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

这可以让你跳右后卫到最后你在的位置,并继续阅读。你显然希望在这里添加很多错误处理,所以无论脚本在哪个点被中断,你都不会处于不一致的状态。

+0

伟大的解决方案,非常整洁优雅。让我通过这个紧缩。 – Robert82

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); 
} 
+0

这与我的想法类似,但有3分钟的超时,我不指望能够一次读取整个文件。有没有办法第二遍回来,然后“跳”到特定的行?假设我第一次得到125,000行,我可以第二次通过125,001行吗? – Robert82

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的内存限制