2013-10-22 52 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的內存限制