PHP Grab last 15 lines in txt file - php

Thank you for taking the time to read this and I will appreciate every single response no mater the quality of content. :)
Using PHP, I'm trying to get the last 15 lines of a text document (.txt) and store that data into a php variable. I understand that this is possible, however when I do get the last 15 lines, is it possible to retain the order? For example:
text document:
A
B
C
When I grab the text document from the last 15 characters, I don't want the echo to end up like:
C
B
A
All assistance is appreciated and I look forward to your replies; thank you. :) If I didn't explain anything clearly and/or you'd like me to explain in more detail, please reply. :)
Thank you.

Try using array_slice, which will return a part of an array. In this case you want it to return the last 15 lines of the array, so:
$filearray = file("filename");
$lastfifteenlines = array_slice($filearray,-15);

If you don't mind loading the entire file into memory:
$lines = array_slice(file('test.txt'), -15);
print_r($lines );
If the file is too large to fit into memory you can use a circular method:
// Read the last $num lines from stream $fp
function read_last_lines($fp, $num)
{
$idx = 0;
$lines = array();
while(($line = fgets($fp)))
{
$lines[$idx] = $line;
$idx = ($idx + 1) % $num;
}
$p1 = array_slice($lines, $idx);
$p2 = array_slice($lines, 0, $idx);
$ordered_lines = array_merge($p1, $p2);
return $ordered_lines;
}
// Open the file and read the last 15 lines
$fp = fopen('test.txt', 'r');
$lines = read_last_lines($fp, 15);
fclose($fp);
// Output array
print_r($lines);
This method will also work if the file has less than 15 lines- returning an array with however many lines are in the file.

You can use fseek with a negative position to seek backwards through the file, counting newlines as you go.
I'm too tired to write up copy/past-able code, but there are some examples in the comments to the manual page for fseek that are very close to what you want.

If the file isn't bigger than available memory you can do this:
$fArray = file("filename");
$len = sizeof($fArray);
for($i=$len -15;$i<$len ;$i++)
{
echo $fArray[$i];
}
If you have a file that is hundreds of megabytes :
$rc = fopen("file","r");
for ($i=0; $line = fgets($file) ;$i++)
{
if ($i%15 == 0)
{
$last15 = array();
}
$last15[] = $line;
}
echo join("\n",$last15);

the longer array solution:
array_slice(explode("\n",file_get_contents($file)),-15);
the shorter array solution:
array_slice(file($file),-15);

This code will open the file, show the total lines, show the header of file and show the last lines of file defined in $limit.
<?php
// open the file in read mode
$file = new SplFileObject('file.csv', 'r');
// get the total lines
$file->seek(PHP_INT_MAX);
$last_line = $file->key();
echo $last_line;
echo "<br>";
// Rewind to first line to get header
$file->rewind();
// Output first line if you need use the header to make something
echo $file->current();
echo "<br>";
// selecting the limit
$limit = 6;
// selecting the last lines using the $limit
$lines = new LimitIterator($file, $last_line - $limit, $last_line);
//print all the last 6 lines array
//print_r(iterator_to_array($lines));
//echo "<br>";
// Loop over whole file to use a single line
foreach ($lines as $line) {
print_r($line);
echo "<br>";
}

Related

Issue with searching for a word from 2 differents text files

I've been pulling my hair for the couple last hours, I can't figure this out, all I'm trying to do is to take 1 line from list.txt file then search for a match in source.txt file, here is my code
<?php
//Let's open the file
$list = #fopen("files/list.txt", "r");
$source = #fopen("files/source.txt", "r");
//I'm calculating number of lines in list.txt
$no_of_lines = count(file('files/list.txt'));
//I created 2 loops
//The first loop is to repeat the process based on the total number of lines in list.txt
//the second loop is the extract only 1 entry from the list.txt and search for a match in source.txt
for ($x=1; $x <= $no_of_lines ; $x++) {
for ($i=1; $i <= 1 ; $i++) {
$getLine = fgets($list);
$matches = array();
if ($source)
{
while (!feof($source))
{
$buffer = fgets($source);
if(strpos($source, $getLine) !== FALSE)
$matches[] = $buffer;
}
fclose($source);
}
}
}
//show results:
print_r($matches);
+source.txt has numbers from 1 to 100, each number in a separate line.
+list.txt has these numbers:
5
20000
1000000
87456
Current Error: Warning: strpos() expects parameter 1 to be string, resource given in C:\laragon\www\SearchFind\test2.php on line 26
I've tried many stackexchange solutions but nothing worked out.
There's no need to count the lines in list.txt first. Just loop calling fgets() until you get to the end of the file.
You need to initialize the $matches array before the loop. Otherwise you clear it out before searching for each number, and the final value will just be the matches for the last number in list.txt.
You need to reopen source.txt each time through the outer loop so you can read from it again.
You should be searching $buffer in the strpos() call, not $source (that's the reason for the error you're getting).
Don't use while (!feof($source)). Use while ($buffer = fgets($source)). fgets() returns FALSE when you get to the end of the file.
You need to use rtrim($getLine) and `rtrim($buffer) to remove the newline at the end of the line, so you're just searching for the number.
for ($i=1; $i <= 1 ; $i++) serves no purpose at all. It just loops one time, which is the same as not looping at all.
If you want to match the whole line, not just look for a substring, use === to compare $getLine and $buffer, not strpos().
<?php
//Let's open the file
$list = fopen("list.txt", "r");
$matches = array();
if ($list) {
while ($getLine = fgets($list)) {
$getLine = rtrim($getLine); // remove newline
$source = fopen("source.txt", "r");
if ($source)
{
while ($buffer = fgets($source)) {
$buffer = rtrim($buffer);
if($buffer === $getLine)
$matches[] = $buffer;
}
fclose($source);
}
}
}
//show results:
print_r($matches);

Read from a text file and post the first 50 lines into another text file?

I have a text file and it is called 'Store.txt'.
I would like to know how I can read from this file and then grab the first 50 lines of numbers/text
and insert them into another text file.
I have little code because I'm not exactly sure how to go about it and I've been searching online but couldn't find much I believe an if statment is the answer?
Any way I have gave it ago but sadly it hasn't worked.
Here is how I got on-
<?php
$fileToOpen = fopen('Store.txt', 'r');
$return = '';
$count = 0;
$return. = $fileToOpen. "\n";
if ($count >= 50)
break;
}
file_put_contents($return, "Store2nd.txt");
fclose($fileToOpen);
?>
Thank you in advance for any help. (:
This will copy upto the first 50 lines without reading in the complete file:
<?php
$fileToOpen = fopen('Store.txt', 'r');
$outputFile = fopen('Store2nd.txt', 'w');
$count = 0;
while (!feof($fileToOpen)) { // We'll copy the whole file ...
if ($count++ >= 50) // ... or the first 50 lines, whichever is less
break;
$line = fgets($fileToOpen);
fwrite($outputFile, $line);
}
fclose($fileToOpen);
fclose($outputFile);
?>
Please give this a try:
<?php
$lines = file('Store.txt'); // make file content an array
$result = array_slice($lines,0,50); // take the first 50 lines
file_put_contents('Store2nd.txt', implode('', $result)); // output
?>
A better way would probably be to do a foreach loop for your text.
Then add $count++ in your loop so that the $count = 0; you've set up will increase.
Right now with your code, nothing is increasing, so $count never reaches 50.
Cheers.

How to sum column text file

Hello everyone and I immediately apologize, as
I have seen various threads on the site, but unfortunately my knowledge is still insufficient to complete my project.
I have a text file and I have to do the sum of each column (just need the total):
1003|name1|1208.00|2.00 |96.00 |0.00|0.00|0.00|0.00|98.00 |90.95 |7.05 |8516.40
1011|name2|1450.00|2.00 |49.00 |0.00|0.00|0.00|0.00|51.00 |44.62 |6.38 |9243.7
1004|name3|1450.00|25.00|170.00|0.00|0.00|0.00|0.00|195.00|175.75|19.25|27912.5 <br>
1002|name4|765.00 |1.00 |17.00 |0.00|0.00|0.00|0.00|18.00 |15.13 |2.87 |2193.26
I need to get this(I have this file on linux then we can use Bash, PHP, Mysql... ):
1003|name1|1208.00|2.00 |96.00 |0.00|0.00|0.00|0.00|98.00 |90.95 |7.05 |8516.40
1011|name2|1450.00|2.00 |49.00 |0.00|0.00|0.00|0.00|51.00 |44.62 |6.38 |9243.7
1004|name3|1450.00|25.00|170.00|0.00|0.00|0.00|0.00|195.00|175.75|19.25|27912.5 <br>
1002|name4|765.00 |1.00 |17.00 |0.00|0.00|0.00|0.00|18.00 |15.13 |2.87 |2193.26 <br>
xxxx|Total |4873.00|30.00|332.00|0.00|0.00|0.00|0.00|362.00 |326.45|35.55|47865.86
Where xxxx is the Id number (No sum here).
I've been trying to do this in PHP and MySQL -- No luck so far.
try something like:
$file = '/path/to/your_file.txt';
if ( ($file = fopen($file, "r")) !== FALSE) {
$total = 0;
$row_1 = 0;
while (($line = fgetcsv($file, 1000, "|")) !== FALSE) {
// brutal dirt sanitization
foreach ( $line as $k => $v ) {
$line[$k] = (float) preg_replace('#[^0-9\.]#','', $v);
}
$total = $total + array_sum(array_slice($line, 2));
$row_1 = $row_1 + array_sum(array_slice($line, 2, 1));
//...
}
echo $total.' | '.$row_1; //...
}
else echo 'error ...';
also, you can sanitize each row by replacing array_sum() by array_map() wih a callback function
Psuedocode:
open source file for reading
open destination file for writing
initialise totaling array to zero values
while not EOF
read in line from file
explode line into working array
for x=2 ; x<14; x++
add totalling array with floatval( working array )
write line out to destination file
close read file
write out totals array to destination file
close destingation file
Try to get the text file data into an excel spreadsheet and then add up the columns.
You can use VB to get the text into excel and then continue adding up the values of each column.
1) replace all | chars with , using str_replace
2) Use str_getcsv to create array out of the above resulting csv string
3) use foreach and loop through each row and calculate total
some PHP code
$str = file_get_contents('myfile.txt');
$str = str_replace('|', ',', $str);
$csv = str_getcsv($str);
$totals = array(0,0,0,0);
foreach ($csv as $row) {
$totals[0] += trim($row[0]);
$totals[1] += trim($row[2]);
$totals[2] += trim($row[3]);
$totals[3] += trim($row[4]);
}
the $totals array contains all totals!

Reading only the last 10 lines of a text file "tail" (Jquery/Ajax/javascript or whatever)

I've been searching for solutions here for a long time now, and i haven't been able to find any so far
what i need is a way to read the last 10 or so lines of a text file, which continually updates every second
i've found some possible codes in php, but then i wont be able to update the file every second.
how do i get only the last 10 lines of a text file using some sort of javascript?
Try using array_slice, which will return a part of an array. In this case you want it to return the last 15 lines of the array,
$filearr = file("filename");
$lastlines = array_slice($filearr,-10);
You can change -10 (-10,-15 any value you want).
Hope it works!
Hmmm... maybe so, for large files:
$fileName = '[path_to_file]';
$file = new \SplFileObject($fileName);
$file->seek($file->getSize());
$lines = $file->key() - 1;
$file->setFlags(\SplFileObject::READ_CSV);
$counter = 0;
$records = 100;
$seek = $lines - $records;
$file->seek($seek);
$return = array();
while ($counter++ < $records) {
try {
$line = $file->fgets();
$return[] = $line;
} catch (\Exception $e) {
}
}
Didn't find the correct answer here, so i started a new question where i'm more specific with what i want
Reading ONLY the last 50 lines of a large text file

PHP: File writing issue

I'm trying to make a function that writes a list of scores for players.
For example:
player_1 100 12 12 10
player_2 39 13 48 29
And when players beat (or do worse) than their previous scores, their score is over-written with the new score.
I've written a function that sort of works, but has multiple problems.
function write($player)
{
global $logfile;
$lines = file($logfile);
foreach($lines as $i => $line)
{
$pieces = explode(" ", $line);
$pieces[0] = trim($pieces[0]);
if( $pieces[0] == $player->name ) //found name
{
trim($lines[$i]);
unset($lines[$i]); //remove the old player data
$lines[$i] = "{$player->name} {$player->lvl} {$player->exp} {$player->mana} \n"; //write the new score
$fp = fopen($logfile,'a');
fwrite($fp,$lines[$i]);
$found = TRUE;
break;
}
}
if(!$found) //record a new player whose score isn't in the file
{
$fp = fopen($logfile,'a');
$newp = "$player->name $player->lvl $player->exp $player->mana \n";
fwrite($fp, $newp);
}
fclose($fp);
}
The file just appends the new score and doesn't overwrite the previous score. Could someone point out my errors?
Try changing:
$fp = fopen($logfile,'w');
into
$fp = fopen($logfile,'a');
in your
if( $pieces[0] == $player->name ) ...
PHP.fopen file open modes ;)
EDIT
You can override your player entry by putting the fwrite() after foreach loop by overriding the whole file with joined lines (this may cause performace issues).
Or
Try to loop line by line using fgets() and then if you will find the right match use fseek() to the previous line and override it ;)
fgets() fseek()
SECOND EDIT
<?php
$find = 'player_1';
$h = fopen('play.txt','r+');
$prev_pos = 0;
while(($line = fgets($h, 4096)) !== false){
$parts = explode(' ', $line);
if($parts[0] == $find) {
fseek($h, $prev_pos);
fwrite($h, "player_222 12 22 411");
break;
}
$prev_pos = ftell($h);
}
fclose($h);
?>
Code sample as requested ;) The idea is to save previous line position and then use it to fseek and override. I'm not sure if the fwrite will work well on all enviroments without PHP_EOL at the end-of-line, but on mine it's fine.
First, let us see the reason why it duplicates the record. $lines is an array in which you are updating the record of the specific player. But after updating the record, you are appending it to the file (using "a" mode) and therefore duplicating the entry of that player.
The idea should be to update that record in the file. And with your logic, the best thing is to rewrite $lines to the file. Since $lines will always contain the updated entry, it makes sense.
Now coming to the logic where you are making an entry for a new player. There is nothing wrong in that logic but it could be improved by appending the new entry to $lines instead of writing to the file.
Here is the updated code. Please note that I've removed lines that weren't needed.
function write($player) {
global $logfile;
$found = FALSE;
$lines = file($logfile);
foreach($lines as $i => $line) {
$pieces = explode(" ", $line);
$pieces[0] = trim($pieces[0]);
if( $pieces[0] == $player->name ) { //found name
$lines[$i] = "{$player->name} {$player->lvl} {$player->exp} {$player->mana} \n"; //write the new score
$found = TRUE;
break;
}
}
if(!$found) { //record a new player whose score isn't in the file
$lines[] = "$player->name $player->lvl $player->exp $player->mana \n";
}
file_put_contents($logfile, $lines);
}
Hope it helps!
Is this code run on a web server with many users accessing at the same time?
If it is, imagine what happens when one user has just opened the file for writing, the file is emptied, and another opens it for reading before the first one has finished writing the data.
A partial solution is write to a temp file and rename the temp as the original when you are done. Rename is atomic, so the users will see either the original file or the new one and not something in between.
But youll still miss some updates. You could lock the file, meaning that when one person is writing another can't read. To do that you would use the flock function: http://php.net/manual/en/function.flock.php
The proper solution is using a real database. Sqlite for example is nice and simple: no external server processes or passwords...

Categories