2012-11-06 152 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; 
}