2012-11-06 148 views
342

如何在PHP中逐行讀取文件,而不將其完全加載到內存中?如何逐行讀取大文件

我的文件太大,無法在內存中打開,所以我總是有內存耗盡錯誤。

文件大小爲1 GB。

+0

看到我的答案來源與這個[鏈接](http://stackoverflow.com/questions/13231547/export-simple-excel-data-into-mysql-using-php/13231633#13231633) –

+5

你應該使用不帶'$ length'參數的'fgets()'。 – Carlos

+14

您想標記爲以下任一答案嗎? –

回答

530

可以使用fgets()功能通過線來讀取文件行:

$handle = fopen("inputfile.txt", "r"); 
if ($handle) { 
    while (($line = fgets($handle)) !== false) { 
     // process the line read. 
    } 

    fclose($handle); 
} else { 
    // error opening the file. 
} 
+3

這是怎麼解釋「在內存中打開太大」的部分? – Starx

+39

您沒有讀取內存中的整個文件。運行所需的最大內存取決於輸入中最長的行。 – codaddict

+8

當然還記得'fclose($ handle);'; 0123; – zelanix

95
if ($file = fopen("file.txt", "r")) { 
    while(!feof($file)) { 
     $line = fgets($file); 
     # do same stuff with the $line 
    } 
    fclose($file); 
} 
+5

由於@ Cuse70在他的回答中表示,如果文件不存在或無法打開,這將導致無限循環。在while循環之前測試'if($ file)' – FrancescoMM

+5

我知道這是舊的,但是:不建議使用while(!feof($ file))。 [看看這裏。](http://stackoverflow.com/questions/5431941/why-is-while-feof-file-always-wrong) –

+0

順便說一句:「如果沒有更多的數據要讀取文件指針,那麼返回FALSE。「 http://php.net/manual/en/function.fgets.php ...以防萬一 – everyman

25

使用緩衝技術來讀取文件。

$filename = "test.txt"; 
$source_file = fopen($filename, "r") or die("Couldn't open $filename"); 
while (!feof($source_file)) { 
    $buffer = fread($source_file, 4096); // use a buffer of 4KB 
    $buffer = str_replace($old,$new,$buffer); 
    /// 
} 
+1

這值得更多的愛,因爲它將與巨大的文件,甚至沒有回車或超長線條的文件... – Jimmery

+0

我不會感到驚訝,如果OP沒有真正關心實際線路,只是想例如提供下載。在這種情況下,這個答案就好了(而且大多數PHP編碼器都會這樣做)。 –

6

需小心,「而(!FEOF ...與fgets()」的東西,與fgets可以得到一個錯誤(returnfing假)和循環永遠沒有達到文件的末尾。codaddict是最接近於正確的但是當你的「而與fgets」循環結束時,檢查FEOF;如果不是真的,那麼你有一個錯誤

-6

函數讀取與陣列回報

function read_file($filename = ''){ 
    $buffer = array(); 
    $source_file = fopen($filename, "r") or die("Couldn't open $filename"); 
    while (!feof($source_file)) { 
     $buffer[] = fread($source_file, 4096); // use a buffer of 4KB 
    } 
    return $buffer; 
} 
+4

這會在內存中創建一個超過一個GB的單個數組(不失爲一個好運),它甚至不是按行分割,而是以任意4096個字符塊分割。你爲什麼要這麼做? – FrancescoMM

67

您可以使用面向對象的接口類的文件 - SplFileObjecthttp://php.net/manual/en/splfileobject.fgets.php(PHP 5> = 5.1.0)

<?php 

$file = new SplFileObject("file.txt"); 

// Loop until we reach the end of the file. 
while (!$file->eof()) { 
    // Echo one line from the file. 
    echo $file->fgets(); 
} 

// Unset the file to call __destruct(), closing the file handle. 
$file = null; 
+2

更清潔的解決方案。謝謝;)還沒有使用這個類,有更多有趣的功能在這裏探索:http://php.net/manual/en/class.splfileobject.php –

+5

謝謝。是的,例如,您可以在添加此行之前,而 $ file-> setFlags(SplFileObject :: DROP_NEW_LINE); 爲了在行尾放置換行符。 – elshnkhll

+0

據我可以看到SplFileObject中沒有'eof()'函數? – Chud37

23

有一個file()函數,返回包含在文件中的線的陣列。

foreach(file('myfile.txt') as $line) { 
    echo $line. "\n"; 
} 
+19

一個GB文件將全部讀入內存並轉換爲多個GB陣列......祝您好運。 – FrancescoMM

+3

這不是所問問題的答案,但它確實回答了許多人在這裏看到的更常見的問題,所以它仍然有用,謝謝。 – pilavdzice

+1

file()對於處理小文件非常方便。特別是當你想要一個數組()作爲最終結果時。 –

5

這個問題的一個流行的解決方案將有新的行字符的問題。用簡單的str_replace就可以很容易地修復它。

$handle = fopen("some_file.txt", "r"); 
if ($handle) { 
    while (($line = fgets($handle)) !== false) { 
     $line = str_replace("\n", "", $line); 
    } 
    fclose($handle); 
} 
11
foreach (new SplFileObject(__FILE__) as $line) { 
    echo $line; 
} 
+0

愛上線 –

14

如果你打開一個大文件,你可能想使用旁邊與fgets發電機(),以避免加載整個文件到內存:

/** 
* @return Generator 
*/ 
$fileData = function() { 
    $file = fopen(__DIR__ . '/file.txt', 'r'); 

    if (!$file) 
     die('file does not exist or cannot be opened'); 

    while (($line = fgets($file)) !== false) { 
     yield $line; 
    } 

    fclose($file); 
}; 

使用方法如下:

foreach ($fileData() as $line) { 
    // $line contains current line 
} 

這樣,您可以處理foreach()中的單個文件行。

注:發電機需要> = 5.5 PHP

5

這我如何與非常大的文件(具有高達100G測試)管理。並且它比fgets更快()

$block =1024*1024;//1MB or counld be any higher than HDD block_size*2 
if($fh = fopen("file.txt", "r")){ 
      $left=''; 
    while (!feof($fh)) {// read the file 
     $temp = fread($fh, $block); 
     $fgetslines = explode("\n",$temp); 
     $fgetslines[0]=$left.$fgetslines[0]; 
     if(!feof($fh))$left = array_pop($lines);   
     foreach($fgetslines as $k => $line){ 
      //do smth with $line 
     } 
    } 
} 
fclose($fh); 
0

SplFileObject在處理大文件時非常有用。

function parse_file($filename) 
{ 
    try { 
     $file = new SplFileObject($filename); 
    } catch (LogicException $exception) { 
     die('SplFileObject : '.$exception->getMessage()); 
    } 
    while ($file->valid()) { 
     $line = $file->fgets(); 
     //do something with $line 
    } 

    //don't forget to free the file handle. 
    $file = null; 
}