2012-06-06 76 views
5

我有一個1.3GB的文本文件,我需要從PHP中提取一些信息。我已經研究過它,並想出了幾種不同的方式來做我需要做的事情,但是一如往常,在稍微澄清一下哪種方法最好,或者如果有另一種更好的存在我不知道?使用PHP從1.3GB文本文件中提取文本的最佳方法?

我在文本文件中需要的信息只是每行的前40個字符,並且文件中有大約1700萬行。每行中的40個字符將被插入到數據庫中。

我的方法如下;

// REMOVE TIME LIMIT 
set_time_limit(0); 
// REMOVE MEMORY LIMIT 
ini_set('memory_limit', '-1'); 
// OPEN FILE 
$handle = @fopen('C:\Users\Carl\Downloads\test.txt', 'r'); 
if($handle) { 
    while(($buffer = fgets($handle)) !== false) { 
     $insert[] = substr($buffer, 0, 40); 
    } 
    if(!feof($handle)) { 
     // END OF FILE 
    } 
    fclose($handle); 
} 

以上就是一次讀取每一行,並獲取數據,我把所有的數據庫插入排序,在交易十次在同一時間做50個插入。

下一個方法與上面的方法相同,但在調用foreach來獲取數據之前調用file()來存儲數組中的所有行?我不確定這種方法,因爲陣列本質上會有1700萬以上的值。

另一種方法是隻提取文件的一部分,用未使用的數據重寫文件,在執行該部分之後,使用header調用調用腳本?

以最迅速和最有效的方式完成此項工作的最佳方法是什麼?還是有更好的方法來處理這個我想到的?

此外,我打算使用這個腳本的瓦特,但在瀏覽器中運行它,而測試已經導致超時的問題,即使將腳本時間設置爲0.有沒有一種方法可以執行腳本來運行,而無需訪問通過瀏覽器頁面?

+0

對於最後一點,'php path/to/script.php'將執行腳本。 – sarnold

+0

@sarnold我只是從命令行做到這一點?謝謝 – Griff

+1

是的,直接從命令行。如果你打算經常通過在腳本的第一行添加'#!/ path/to/php',然後運行'chmod 755 path/to/script'或'chmod 500'或任何適當的權限,你想要的。 – sarnold

回答

5

到目前爲止,它不錯,不要使用「file()」函數,因爲它最有可能達到RAM使用限制並終止您的腳本。

我甚至不會在「insert []」數組中堆積東西,因爲這樣也會浪費RAM。如果可以,請立即插入數據庫。

順便說一句,有一個很好的工具稱爲「剪切」,你可以用它來處理文件。

cut -c1-40 file.txt 

你甚至可以將cut的stdout重定向到一些插入數據庫的PHP腳本。

cut -c1-40 file.txt | php -f inserter.php 

inserter.php然後可以從php:// stdin讀取行並插入到數據庫中。如果你使用Windows,你可以使用MinGW shell,或者作爲msystools的一部分(如果你使用git)或者使用gnuWin32安裝原生win32應用程序,那麼「cut」是所有Linux上可用的標準工具。

+0

對於mysql來說,這不會太過分嗎?一次插入1700萬次或50次?插入50後,陣列重置。 – Griff

+0

@Griff,這是PHP的數組訪問與MySQL插入速度。這可能會更快,但也可能會更慢。唯一的方法來檢查這將是基準。另外,僅使用準備好的INSERT語句來更改參數可能會有所幫助。 –

+0

感謝您提供此信息。將立即開始工作,並讓你知道我如何得到:) – Griff

2

爲什麼在你的RDBMS幾乎可以肯定具有批量導入功能的情況下在PHP中執行此操作?例如,MySQL有LOAD DATA INFILE

LOAD DATA INFILE 'data.txt' 
INTO TABLE `some_table` 
    FIELDS TERMINATED BY '' 
    LINES TERMINATED BY '\n'; 
    (@line) 
SET `some_column` = LEFT(@line, 40); 

一個查詢。

MySQL還有一個mysqlimport實用程序,該實用程序從命令行包裝此功能。

+0

我的共享主機不允許使用'INFILE',這是我的第一選擇。 – Griff

1

以上都不是。使用fgets()的問題是,它不能像你期望的那樣工作。達到最大字符數時,下一次撥打fgets()將繼續在同一行上。您已正確識別使用file()的問題。第三種方法是一個有趣的想法,您也可以使用其他解決方案。

也就是說,你的第一個想法是使用fgets()非常接近,但我們需要稍微修改它的行爲。這是一個定製的版本,可以像你期望的那樣工作:

function fgetl($fp, $len) { 
    $l = 0; 
    $buffer = ''; 
    while (false !== ($c = fgetc($fp)) && PHP_EOL !== $c) { 
     if ($l < $len) 
      $buffer .= $c; 
     ++$l; 
    } 
    if (0 === $l && false === $c) { 
     return false; 
    } 
    return $buffer; 
} 

立即執行插入操作,否則會浪費內存。確保你使用prepared statements來插入這麼多行;這將大大縮短執行時間。當您只能提交數據時,您不希望在每個插入提交完整查詢。

+0

從PHP 4.3.0開始,這仍然是個問題嗎?另外,速度如何與76倍的函數調用? – Wiseguy

+0

我相信這是fgets()的預期行爲。速度不應該是一個問題,如果你正在使用準備好的語句(http://php.net/manual/en/pdo.prepared-statements.php) – siimsoni

+0

@KSiimson我正在使用'PDO'準備語句,@Wiseguy這是我認爲省略「length」屬性會按我的意願去做? – Griff

相關問題