Read file lines backwards (fgets) with php - php

I have a txt file that I want to read backwards, currently I'm using this:
$fh = fopen('myfile.txt','r');
while ($line = fgets($fh)) {
echo $line."<br />";
}
This outputs all the lines in my file.
I want to read the lines from bottom to top.
Is there a way to do it?

First way:
$file = file("test.txt");
$file = array_reverse($file);
foreach($file as $f){
echo $f."<br />";
}
Second Way (a):
To completely reverse a file:
$fl = fopen("\some_file.txt", "r");
for($x_pos = 0, $output = ''; fseek($fl, $x_pos, SEEK_END) !== -1; $x_pos--) {
$output .= fgetc($fl);
}
fclose($fl);
print_r($output);
Second Way (b):
Of course, you wanted line-by-line reversal...
$fl = fopen("\some_file.txt", "r");
for($x_pos = 0, $ln = 0, $output = array(); fseek($fl, $x_pos, SEEK_END) !== -1; $x_pos--) {
$char = fgetc($fl);
if ($char === "\n") {
// analyse completed line $output[$ln] if need be
$ln++;
continue;
}
$output[$ln] = $char . ((array_key_exists($ln, $output)) ? $output[$ln] : '');
}
fclose($fl);
print_r($output);

Try something simpler like this..
print_r(array_reverse(file('myfile.txt')));

Here is my solution for just printing the file backwards. It is quite memory-friendly. And seems more readable (IMO [=in my opinion]).
It goes through the file backwards, count the characters till start of a line or start of the file and then reads and prints that amount of characters as a line, then moves cursor back and reads another line like that...
if( $v = #fopen("PATH_TO_YOUR_FILE", 'r') ){ //open the file
fseek($v, 0, SEEK_END); //move cursor to the end of the file
/* help functions: */
//moves cursor one step back if can - returns true, if can't - returns false
function moveOneStepBack( &$f ){
if( ftell($f) > 0 ){ fseek($f, -1, SEEK_CUR); return true; }
else return false;
}
//reads $length chars but moves cursor back where it was before reading
function readNotSeek( &$f, $length ){
$r = fread($f, $length);
fseek($f, -$length, SEEK_CUR);
return $r;
}
/* THE READING+PRINTING ITSELF: */
while( ftell($v) > 0 ){ //while there is at least 1 character to read
$newLine = false;
$charCounter = 0;
//line counting
while( !$newLine && moveOneStepBack( $v ) ){ //not start of a line / the file
if( readNotSeek($v, 1) == "\n" ) $newLine = true;
$charCounter++;
}
//line reading / printing
if( $charCounter>1 ){ //if there was anything on the line
if( !$newLine ) echo "\n"; //prints missing "\n" before last *printed* line
echo readNotSeek( $v, $charCounter ); //prints current line
}
}
fclose( $v ); //close the file, because we are well-behaved
}
Of course replace PATH_TO_YOUR_FILE with your own path to your file, # is used when opening the file, because when the file is not found or can't be opened - warning is raised - if you want to display this warning - just remove the error surpressor '#'.

If the file is not so big you can use file():
$lines = file($file);
for($i = count($lines) -1; $i >= 0; $i--){
echo $lines[$i] . '<br/>';
}
However, this requires the whole file to be in memory, that's why it is not suited for really large files.

Here's my simple solution without messing up anything or adding more complex code
$fh = fopen('myfile.txt','r');
while ($line = fgets($fh)) {
$result = $line . "<br>" . $result;
}
echo $result // or return $result if you are using it as a function

Related

PHP: feof miss last word

the problem is simple but complicated at the same time.
feof doesn't print my last word. It take from file name city and code (Venice,A908) and should show in OUTPUT: nameCity,codeOfCity.
Let me show you an example:
City.csv
Abano Terme,A001
Abbadia Cerreto,A004
Abbadia Lariana,A005
Abbiategrasso,A010
Zubiena,M196
Zuccarello,M197
Zuclo,M198
Zungri,M204
Code:
<?php
$buffer = "";
$file = fopen("City.csv", "r");
//while (($c = fgetc($file)) != EOF )
//while (($c = fgetc($file)) != NULL )
//while (($c = fgetc($file)) !== false )
while(!feof($file))
{
$c = fgetc($file);
$buffer .= $c;
if($c == ",")
{
echo $buffer;
$buffer = "";
}
if($c == "\n")
{
echo $buffer."<br/>";
$buffer = "";
}
}
fclose($file);
?>
OUTPUT:
Abano Terme,A001
Abbadia Cerreto,A004
Abbadia Lariana,A005
Abbiategrasso,A010
Zubiena,M196
Zuccarello,M197
Zuclo,M198
Zungri,
Since it seems like you are just trying to output the file as is, with only change being to substitute HTML line breaks <br /> instead of new line characters why not simplify things?
echo nl2br(file_get_contents('City.csv'), true);
Or if you don't want to read the whole file into memory:
$file = fopen('City.csv', 'r');
while(!feof($file)) {
echo nl2br(fgets($file), true);
}
fclose($file);
In one of the comments above you mention that you want the city and city values available as variables (though your code example doesn't seem to indicate this). If that is the case, try fgetcsv() like this:
$file = fopen('City.csv', 'r');
while($values = fgetcsv($file)) {
$city = $values[0];
$city_code = $values[1];
echo $city . ',' . $city_code . '<br />';
}
fclose($file);
Your problem is, there's no newline at the end of your file, so it never hits the last "\n" check to output the buffer contents.
to fix this, you just need to put in another check on that conditional. change
if($c == "\n")
to:
if($c == "\n" || feof($file))
Here's a much cleaner and more concise version of your code if you'd like to use the correct function for parsing a csv file:
<?php
$buffer = array();
$file = fopen("City.csv", "r");
while(!feof($file) && $buffer[] = fgetcsv($file));
fclose($file);
foreach($buffer as $line){
echo join(',', $line).'<br/>';
}
?>

How can I most efficiently read the first n lines of a large text file in php?

So far I'm able to return the entire file, or an empty string. But not the first n lines of a file as expected.
echo head('/path/to/my_file.txt',100); // returns '', or in other versions `1\n1\n...`
function head($filepath, $lines = 1, $adaptive = true) {
// Open file
$f = #fopen($filepath, "rb");
if ($f === false) return false;
// Sets buffer size
if (!$adaptive) $buffer = 4096;
else $buffer = ($lines < 2 ? 64 : ($lines < 10 ? 512 : 4096));
// Start reading
$output = '';
while($line = fgets($f,$buffer) !== false) {
if (feof($f)) break;
$output .= $line . "\n";
}
// Close file and return
fclose($f);
return trim($output);
}
In contrast, the following tail function (source) works perfectly:
function tail($filepath, $lines = 1, $adaptive = true) {
// Open file
$f = #fopen($filepath, "rb");
if ($f === false) return false;
// Sets buffer size
if (!$adaptive) $buffer = 4096;
else $buffer = ($lines < 2 ? 64 : ($lines < 10 ? 512 : 4096));
// Jump to last character
fseek($f, -1, SEEK_END);
// Read it and adjust line number if necessary
// (Otherwise the result would be wrong if file doesn't end with a blank line)
if (fread($f, 1) != "\n") $lines -= 1;
// Start reading
$output = '';
$chunk = '';
// While we would like more
while (ftell($f) > 0 && $lines >= 0) {
// Figure out how far back we should jump
$seek = min(ftell($f), $buffer);
// Do the jump (backwards, relative to where we are)
fseek($f, -$seek, SEEK_CUR);
// Read a chunk and prepend it to our output
$output = ($chunk = fread($f, $seek)) . $output;
// Jump back to where we started reading
fseek($f, -mb_strlen($chunk, '8bit'), SEEK_CUR);
// Decrease our line counter
$lines -= substr_count($chunk, "\n");
}
// While we have too many lines
// (Because of buffer size we might have read too many)
while ($lines++ < 0) {
// Find first newline and remove all text before that
$output = substr($output, strpos($output, "\n") + 1);
}
// Close file and return
fclose($f);
return trim($output);
}
// here the correction => apply parenthesis before ...
while(($line = fgets($f)) !== false) {
if (feof($f)) break;
if ($lines-- == 0) break;
$output .= $line . "\n";
}
You just need to keep a count of the number of lines read, and abort after $lines is reached.
$i = 0;
while($line = fgets($file) !== false) {
if (feof($file)) break;
$output .= $line . "\n";
if ($i++ >= $lines) {
break;
}
}

Reading Specific Lines In Text File With PHP

I would like to read from lines 51 to 100 in a text file using PHP.
The text file contains less than 500 lines at any given time.
I want to read from lines 51 to 100. No other lines.
I hope you understand the question. I've used SO many times and love it. Just cannot find an answer to this one anywhere.
Any help will be appreciated. Thanks in advance.
Just posting another example here based off Guilherme's example :
After testing Guilherme's code on my box, I found that it was having errors pertaining to the use of feof() while his code should work (the logic is sound), it does break.
The following method (used the same way as his), should be a working drop-in replacement.
function retrieveText($file, $min, $max)
{
$output = Array();
$line = -1;
$handle = fopen($file, 'r');
while(!feof($handle)) {
$line++;
if(($line >= $min && $line <= $max)) {
$output[] = fgets($handle);
} elseif($line > $max) {
break;
} else {
fgets($handle);
}
}
fclose($handle);
return implode("\n", $output);
}
Please note, this uses a carriage return to separate each line "\n", which you can alter to "<br />" or whatever you like depending on where you will display the result.
Use while, fopen and feof (it is good for read big files 8) ), like this:
<?php
function retrieveText($file, $init, $end, $sulfix = '')
{
$i = 1;
$output = '';
$handle = fopen($file, 'r');
while (false === feof($handle) && $i <= $end) {
$data = fgets($handle);
if ($i >= $init) {
$output .= $data . $sulfix;
}
$i++;
}
fclose($handle);
return $output;
}
Example on how to use this function to get lines 51 to 100 :
echo retrieveText('myfile.txt', 51, 100);
Add break line:
echo retrieveText('myfile.txt', 51, 100, PHP_EOL);
Add break line in html:
echo retrieveText('myfile.txt', 51, 100, '<br>');

What function should I use to read the 5th line of a large file?

What php function should I use to count the 5th line of a large file as the 5th line is the bandwidth?
Example of data:
103.239.234.105 -- [2007-04-01 00:42:21] "GET articles/learn_PHP_basics HTTP/1.0" 200 12729 "Mozilla/4.0"
If you want to read every 5th line, you could use an SplFileObject to make life a little easier (than the fopen/fgets/fclose family of functions).
$f = new SplFileObject('myreallybigfile.txt');
// Read ahead so that if the last line in the file is a 5th line, we echo it.
$f->setFlags(SplFileObject::READ_AHEAD);
// Loop over every 5th line starting at line 5 (offset 4).
for ($f->rewind(), $f->seek(4); $f->valid(); $f->seek($f->key()+5)) {
echo $f->current();
}
Open the file into a handle:
$handle = fopen("someFilePath", "r");
Then read the first 5 lines, and only save the fifth:
$i = 0;
$fifthLine = null;
while (($buffer = fgets($handle, 4096)) !== false) {
if $i++ >= 5) {
$fifthLine = $buffer;
break;
}
}
fclose($handle);
if ($i < 5); // uh oh, there weren't 5 lines in the file!
//$fifthLine should contain the 5th line in the file
Note that this is streamed so it doesn't load the whole file.
http://tekkie.flashbit.net/php/tail-functionality-in-php
<?php
// full path to text file
define("TEXT_FILE", "/home/www/default-error.log");
// number of lines to read from the end of file
define("LINES_COUNT", 10);
function read_file($file, $lines) {
//global $fsize;
$handle = fopen($file, "r");
$linecounter = $lines;
$pos = -2;
$beginning = false;
$text = array();
while ($linecounter > 0) {
$t = " ";
while ($t != "\n") {
if(fseek($handle, $pos, SEEK_END) == -1) {
$beginning = true;
break;
}
$t = fgetc($handle);
$pos --;
}
$linecounter --;
if ($beginning) {
rewind($handle);
}
$text[$lines-$linecounter-1] = fgets($handle);
if ($beginning) break;
}
fclose ($handle);
return array_reverse($text);
}
$fsize = round(filesize(TEXT_FILE)/1024/1024,2);
echo "<strong>".TEXT_FILE."</strong>\n\n";
echo "File size is {$fsize} megabytes\n\n";
echo "Last ".LINES_COUNT." lines of the file:\n\n";
$lines = read_file(TEXT_FILE, LINES_COUNT);
foreach ($lines as $line) {
echo $line;
}

Read a file backwards line by line using fseek

How do I read a file backwards line by line using fseek?
code can be helpful. must be cross platform and pure php.
many thanks in advance
regards
Jera
If you are going to read the entire file in anyways, just use file() to read the file in as an array (each line is each element in the array) and then use array_reverse() to flip the array backwards and loop through that. Or just do a reverse for loop where you start at the end and decrement on each loop.
$file = file("test.txt");
$file = array_reverse($file);
foreach($file as $f){
echo $f."<br />";
}
The question is asking using fseek, so can only assume that performance is an issue and file() is not the solution. Here is a simple approach using fseek:
My file.txt
#file.txt
Line 1
Line 2
Line 3
Line 4
Line 5
And the code:
<?php
$fp = fopen('file.txt', 'r');
$pos = -2; // Skip final new line character (Set to -1 if not present)
$lines = array();
$currentLine = '';
while (-1 !== fseek($fp, $pos, SEEK_END)) {
$char = fgetc($fp);
if (PHP_EOL == $char) {
$lines[] = $currentLine;
$currentLine = '';
} else {
$currentLine = $char . $currentLine;
}
$pos--;
}
$lines[] = $currentLine; // Grab final line
var_dump($lines);
Output:
array(5) {
[0]=>
string(6) "Line 5"
[1]=>
string(6) "Line 4"
[2]=>
string(6) "Line 3"
[3]=>
string(6) "Line 2"
[4]=>
string(6) "Line 1"
}
You don't have to append to the $lines array like I am, you can print the output straight away if that is the purpose of your script. Also it is easy to introduce a counter if you want to limit the number of lines.
$linesToShow = 3;
$counter = 0;
while ($counter <= $linesToShow && -1 !== fseek($fp, $pos, SEEK_END)) {
// Rest of code from example. After $lines[] = $currentLine; add:
$counter++;
}
<?php
class ReverseFile implements Iterator
{
const BUFFER_SIZE = 4096;
const SEPARATOR = "\n";
public function __construct($filename)
{
$this->_fh = fopen($filename, 'r');
$this->_filesize = filesize($filename);
$this->_pos = -1;
$this->_buffer = null;
$this->_key = -1;
$this->_value = null;
}
public function _read($size)
{
$this->_pos -= $size;
fseek($this->_fh, $this->_pos);
return fread($this->_fh, $size);
}
public function _readline()
{
$buffer =& $this->_buffer;
while (true) {
if ($this->_pos == 0) {
return array_pop($buffer);
}
if (count($buffer) > 1) {
return array_pop($buffer);
}
$buffer = explode(self::SEPARATOR, $this->_read(self::BUFFER_SIZE) . $buffer[0]);
}
}
public function next()
{
++$this->_key;
$this->_value = $this->_readline();
}
public function rewind()
{
if ($this->_filesize > 0) {
$this->_pos = $this->_filesize;
$this->_value = null;
$this->_key = -1;
$this->_buffer = explode(self::SEPARATOR, $this->_read($this->_filesize % self::BUFFER_SIZE ?: self::BUFFER_SIZE));
$this->next();
}
}
public function key() { return $this->_key; }
public function current() { return $this->_value; }
public function valid() { return ! is_null($this->_value); }
}
$f = new ReverseFile(__FILE__);
foreach ($f as $line) echo $line, "\n";
To completely reverse a file:
$fl = fopen("\some_file.txt", "r");
for($x_pos = 0, $output = ''; fseek($fl, $x_pos, SEEK_END) !== -1; $x_pos--) {
$output .= fgetc($fl);
}
fclose($fl);
print_r($output);
Of course, you wanted line-by-line reversal...
$fl = fopen("\some_file.txt", "r");
for($x_pos = 0, $ln = 0, $output = array(); fseek($fl, $x_pos, SEEK_END) !== -1; $x_pos--) {
$char = fgetc($fl);
if ($char === "\n") {
// analyse completed line $output[$ln] if need be
$ln++;
continue;
}
$output[$ln] = $char . ((array_key_exists($ln, $output)) ? $output[$ln] : '');
}
fclose($fl);
print_r($output);
Really though, Jonathan Kuhn has the best answer IMHO above. The only cases you'd not use his answer that I know of is if file or like functions are disabled via php.ini, yet the admin forgot about fseek, or when opening a huge file just get the last few lines of contents would magically save memory this way.
Note: Error handling not included. And, PHP_EOL didn't cooperate, so I used "\n" to denote end of line instead. So, above may not work in all cases.
You cannot fseek line by line, because you do not know how long the lines are until you read them.
You should either read the whole file into a list of lines, or if the file is too big for that and you only need the last lines, read fixed-sized chunks from the end of the file and implement a bit more complicated logic which detects lines from such data.
Reading the entire file into an array and reversing is fine unless the file is enormous.
You could perform a buffered read of your file from back to front with something like this:
establish a buffer_size B - this should be longer than the longest anticipated line otherwise you'll need some logic for growing the buffer size when lines are too long
set offset = file length - buffer_size
while the offset>=0
read buffer_size bytes from offset
read a line - it will be incomplete as we'll have jumped into the middle of a line, so we want to ensure the next buffer we read ends with it. Set offset = offset - buffer_size + line length
discard that line, read all following lines into an array and reverse them
process this array to do whatever you wanted to do
This code read file backwards. This code ignore modifications on reading, example apache access.log new lines on procressing.
$f = fopen('FILE', 'r');
fseek($f, 0, SEEK_END);
$pos = ftell($f);
$pos--;
while ($pos > 0) {
$chr = fgetc($f);
$pos --;
fseek($f, $pos);
if ($chr == PHP_EOL) {
YOUR_OWN_FUNCTION($rivi);
$rivi = NULL;
continue;
}
$rivi = $chr.$rivi;
}
fclose($f);
I know this has been answered already but I found another, maybe faster, way.
// Read last 5000 chars of 'foo.log'
if(file_exists('foo.log') && $file = fopen('foo.log', 'r')) {
fseek($file, -5000, SEEK_END);
$text = stream_get_line($file, 5000);
var_dump($text);
fclose($file);
}
Here's a drop in replacement(ish) for fgets($fp) called fgetsr() that reads lines from a file in reverse order.
This code is verbatim so you should (famous last words) be able to copy it into a file on your server and run it. Though you may well need to change the filename in the fopn() call.
<?php
header('Content-Type: text/plain');
$fp = fopen('post.html', 'r');
while($line = fgetsr($fp)) {
echo $line;
}
// Read a line from the file but starting from the end
//
// #param $fp integer The file pointer
//
function fgetsr($fp)
{
// Make this variable persistent inside this function
static $seeked;
// The line buffer that will eventually be returned
$line = '';
// Initially seek to the end of the file
if (!$seeked) {
fseek($fp, -1, SEEK_END);
$seeked = true;
}
// Loop through all of the characters in the file
while(strlen($char = fgetc($fp)) {
// fgetc() advances that pointer so go back TWO places
// instead of one
fseek($fp, -2, SEEK_CUR);
//
// Check for a newline (LF). If a newline is found
// then break out of the function and return the
// line that's stored in the buffer.
//
// NB The first line in the file (ie the last to
// be read)has a special case
//
if (ftell($fp) <= 0) {
fseek($fp, 0, SEEK_SET);
$line = fgets($fp);
fseek($fp, 0, SEEK_SET);
return $line;
} else if ($char === "\n") {
$line = strrev($line);
return $line . "\n";
} else {
$line .= $char;
}
}
}
?>
Functions to read a file line-by-line in reverse:
function revfopen($filepath, $mode)
{
$fp = fopen($filepath, $mode);
fseek($fp, -1, SEEK_END);
if (fgetc($fp) !== PHP_EOL) {
fseek($fp, 1, SEEK_END);
}
return $fp;
}
function revfgets($fp)
{
$s = '';
while (true) {
if (fseek($fp, -2, SEEK_CUR) === -1) {
return false;
}
if (($c = fgetc($fp)) === PHP_EOL) {
break;
}
$s = $c . $s;
}
return $s;
}
Example use case: parse a long file until some date:
$fp = revfopen('/path/to/file', 'r');
$buffer = '';
while (($line = revfgets($fp)) !== false) {
if (strpos($line, '05-10-2021') === 0) {
break;
}
array_unshift($buffer, $line);
}
echo implode("\n", $buffer);

Categories