599

I want to read a file line by line, but without completely loading it in memory.

My file is too large to open in memory, and if try to do so I always get out of memory errors.

The file size is 1 GB.

adnan masood
  • 6,015
  • 2
  • 13
  • 6

15 Answers15

848

You can use the fgets() function to read the file line by line:

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

    fclose($handle);
}
Your Common Sense
  • 156,878
  • 40
  • 214
  • 345
codaddict
  • 445,704
  • 82
  • 492
  • 529
  • 3
    How does this account for the `too large to open in memory` part? – Starx Nov 06 '12 at 07:53
  • 83
    You are not reading the entire file in memory. The max memory needed to run this depends on the longest line in the input. – codaddict Nov 06 '12 at 07:54
  • For a way to read the file while uploading it, try the answer to [How does one upload a .txt file in PHP and have it read line by line on another page?](http://stackoverflow.com/a/13176687/662581) – Michel Ayres May 14 '14 at 14:55
  • 13
    @Brandin - Moot - In those situations, the asked question, which is to read a file LINE BY LINE, does not have a well-defined result. – ToolmakerSteve Jun 30 '16 at 18:57
  • 3
    @ToolmakerSteve Then define what should happen. If you want you can just print the message "Line too long; giving up." and that is a well-defined result too. – Brandin Jun 30 '16 at 19:52
  • 2
    Can a line contain a boolean false? If so then this method would stop without reaching the end of file. The Example #1 on this URL http://php.net/manual/en/function.fgets.php suggests that fgets sometimes can return boolean false even though end of file has yet not been reached. In the comment section on that page people report that fgets() doesn't always return correct values, so it's safer to use feof as the loop conditional. – cjohansson Dec 07 '16 at 11:21
  • @cjohansson is correct that FALSE is returned on EoF but also on error by fgets. Though -reader beware that- feof also returns FALSE on error as well as not being at EoF yet. Checking both the fgets !== FALSE and, after exiting the while, ensure feof === TRUE may be preferable. – NekoKikoushi Jun 25 '20 at 19:32
140
if ($file = fopen("file.txt", "r")) {
    while(!feof($file)) {
        $line = fgets($file);
        # do same stuff with the $line
    }
    fclose($file);
}
cjohansson
  • 1,058
  • 10
  • 13
Syuaa SE
  • 1,631
  • 1
  • 9
  • 2
  • 14
    I know this is old, but: using while(!feof($file)) isn't recommended. [Have a look here.](http://stackoverflow.com/questions/5431941/why-is-while-feof-file-always-wrong) – Kevin Van Ryckegem Nov 14 '15 at 22:34
  • 1
    BTW: "If there is no more data to read in the file pointer, then FALSE is returned." http://php.net/manual/en/function.fgets.php ... Just in case – everyman Jan 27 '16 at 10:33
  • 2
    `feof()` doesn't exist anymore? – Ryan DuVal Jul 15 '16 at 16:39
133

You can use an object oriented interface class for a file - SplFileObject http://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;
Will
  • 24,082
  • 14
  • 97
  • 108
elshnkhll
  • 2,077
  • 1
  • 19
  • 24
  • 3
    much cleaner solution. thanks ;) haven't used this class yet, there are more interesting functions here to explore: http://php.net/manual/en/class.splfileobject.php – Lukas Liesis May 10 '15 at 19:29
  • 10
    Thanks. Yes, for example you can add this line before while $file->setFlags(SplFileObject::DROP_NEW_LINE); in order to drop newlines at the end of a line. – elshnkhll Nov 09 '15 at 18:22
  • 3
    Thanks! Also, use `rtrim($file->fgets())` to strip trailing newlines for each line string that is read if you don't want them. – racl101 Nov 22 '17 at 23:32
  • @Chud37 yes there is: https://www.php.net/manual/en/splfileobject.eof.php – Nathan F. Dec 09 '19 at 15:19
  • 2
    Even shorter: `foreach (new SplFileObject('file.txt') as $line) echo $line` – jcsahnwaldt Reinstate Monica Oct 02 '21 at 18:55
94

If you want to use foreach instead of while when opening a big file, you probably want to encapsulate the while loop inside a Generator to avoid loading the whole file into memory:

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

    if (!$file) {
        return; // die() is a bad practice, better to use return
    }    
    while (($line = fgets($file)) !== false) {
        yield $line;
    }

    fclose($file);
};

Use it like this:

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

This way you can process individual file lines inside the foreach().

Note: Generators require >= PHP 5.5

Your Common Sense
  • 156,878
  • 40
  • 214
  • 345
The Onin
  • 5,068
  • 2
  • 38
  • 55
  • 5
    This should be an accepted answer instead. Its hundred times faster with generators. – Tachi Apr 12 '18 at 08:06
  • 3
    And waaay more memory-efficient. – The Onin May 27 '18 at 11:27
  • 2
    @NinoŠkopac: Can you explain why this solution is more memory-efficient? For instance, in comparison to the `SplFileObject` approach. – k00ni Apr 24 '20 at 11:12
  • 1
    Not sure what Tachi and The Onin's comments are comparing against, but I ran this against a 90MB text file, compared with codadict's method and found this to be 44% slower and use the same amount of memory. (ran on PHP 7.3) – artfulrobot Apr 14 '21 at 16:50
  • @Tachi you are probably confusing something. This solution is neither "faster" or "slower" than the code in the accepted answer, let alone "hundred times". That's just a solution that allows one to use foreach instead of while, which *looks nicer* but inside it is using **exactly the same `while` loop** used in other answers – Your Common Sense May 10 '22 at 13:07
37

There is a file() function that returns an array of the lines contained in the file.

foreach(file('myfile.txt') as $line) {
   echo $line. "\n";
}
Funk Forty Niner
  • 74,450
  • 15
  • 68
  • 141
NoImaginationGuy
  • 1,795
  • 14
  • 24
32

The obvious answer wasn't there in all the responses.
PHP has a neat streaming delimiter parser available made for exactly that purpose.

$fp = fopen("/path/to/the/file", "r");
while (($line = stream_get_line($fp, 1024 * 1024, "\n")) !== false) {
  echo $line;
}
fclose($fp);
John
  • 7,507
  • 3
  • 52
  • 52
31

Use buffering techniques to read the file.

$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);
    ///
}
Starx
  • 77,474
  • 47
  • 185
  • 261
  • 2
    this deserves more love, as it will work with huge files, even files that have no carriage returns or exceedingly long lines... – Jimmery Jun 11 '15 at 13:50
  • I wouldn't be surprised if the OP didn't really care about actual lines and just wanted to e.g. serve a download. In that case, this answer is just fine (and what most PHP coders would do anyway). – Álvaro González Jan 19 '16 at 10:46
  • Sir, How will you locate the file inside the fopen() by the way? Assuming we need to specify the url for opening! – Deepak Keynes Feb 25 '21 at 07:48
25
foreach (new SplFileObject(__FILE__) as $line) {
    echo $line;
}
Quolonel Questions
  • 6,603
  • 2
  • 32
  • 33
10

One of the popular solutions to this question will have issues with the new line character. It can be fixed pretty easy with a simple str_replace.

$handle = fopen("some_file.txt", "r");
if ($handle) {
    while (($line = fgets($handle)) !== false) {
        $line = str_replace("\n", "", $line);
    }
    fclose($handle);
}
Tegan Snyder
  • 785
  • 7
  • 12
10

This how I manage with very big file (tested with up to 100G). And it's faster than 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);
barsdeveloper
  • 930
  • 8
  • 28
Metodi Darzev
  • 117
  • 1
  • 5
9

SplFileObject is useful when it comes to dealing with large files.

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;
}
xanadev
  • 751
  • 9
  • 26
8

Be careful with the 'while(!feof ... fgets()' stuff, fgets can get an error (returnfing false) and loop forever without reaching the end of file. codaddict was closest to being correct but when your 'while fgets' loop ends, check feof; if not true, then you had an error.

Cuse70
  • 271
  • 3
  • 5
2
<?php
echo '<meta charset="utf-8">';

$k= 1;
$f= 1;
$fp = fopen("texttranslate.txt", "r");
while(!feof($fp)) {
    $contents = '';
    for($i=1;$i<=1500;$i++){
        echo $k.' -- '. fgets($fp) .'<br>';$k++;
        $contents .= fgets($fp);
    }
    echo '<hr>';
    file_put_contents('Split/new_file_'.$f.'.txt', $contents);$f++;
}
?>
-1

You can use the fgets() function in combination with a loop:

$filename = 'path/to/your/file.txt';
$handle = fopen($filename, 'r');

if ($handle) {
    while (($line = fgets($handle)) !== false) {
        // Process the current line
        echo $line;

        // You can perform any desired operations on the line here
        // For example, you can parse and extract data from each line

        // Proceed to the next line
    }

    fclose($handle);
}


Jamil
  • 104
  • 2
-9

Function to Read with array return

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;
}
Yaroslav Shabalin
  • 1,645
  • 3
  • 17
  • 29
  • 7
    This would create a single array of more than one GB in memory (good luck with it) divided not even in lines but in arbitrary 4096 character chunks. Why on earth would you want to do that? – FrancescoMM Apr 15 '15 at 09:09