replacing a single line of a .txt file using php - php

I am trying to use a php call through AJAX to replace a single line of a .txt file, in which I store user-specific information. The problem is that if I use fwrite once getting to the correct line, it leaves any previous information which is longer than the replacement information untouched at the end. Is there an easy way to clear a single line in a .txt file with php that I can call first?
Example of what is happening - let's say I'm storing favorite composer, and a user has "Beethoven" in their .txt file, and want's to change it to "Mozart", when I used fwrite over "Beethoven" with "Mozart", I am getting "Mozartven" as the new line. I am using "r+" in the fopen call, as I only want to replace a single line at a time.

If this configuration data doesn't need to be made available to non-PHP apps, consider using var_export() instead. It's basically var_dump/print_r, but outputs the variable as parseable PHP code. This'd reduce your code to:
include('config.php');
$CONFIG['musician'] = 'Mozart';
file_put_contents('config.php', '<?php $CONFIG = ' . var_export($CONFIG, true));

This is a code I've wrote some time ago to delete line from the file, it have to be modified. Also, it will work correctly if the new line is shorter than the old one, for longer lines heavy modification will be required.
The key is the second while loop, in which all contents of the file after the change is being rewritten in the correct position in the file.
<?php
$size = filesize('test.txt');
$file = fopen('test.txt', 'r+');
$lineToDelete = 3;
$counter = 1;
while ($counter < $lineToDelete) {
fgets($file); // skip
$counter++;
}
$position = ftell($file);
$lineToRemove = fgets($file);
$bufferSize = strlen($lineToRemove);
while ($newLine = fread($file, $bufferSize)) {
fseek($file, $position, SEEK_SET);
fwrite($file, $newLine);
$position = ftell($file);
fseek($file, $bufferSize, SEEK_CUR);
}
ftruncate($file, $size - $bufferSize);
echo 'Done';
fclose($file);
?>

Related

PHP File Handling (Download Counter) Reading file data as a number, writing it as that plus 1

I'm trying to make a download counter in a website for a video game in PHP, but for some reason, instead of incrementing the contents of the downloadcount.txt file by 1, it takes the number, increments it, and appends it to the end of the file. How could I just make it replace the file contents instead of appending it?
Here's the source:
<?php
ob_start();
$newURL = 'versions/v1.0.0aplha/Dungeon1UP.zip';
//header('Location: '.$newURL);
//increment download counter
$file = fopen("downloadcount.txt", "w+") or die("Unable to open file!");
$content = fread($file,filesize("downloadcount.txt"));
echo $content;
$output = (int) $content + 1;
//$output = 'test';
fwrite($file, $output);
fclose($file);
ob_end_flush();
?>
The number in the file is supposed to increase by one every time, but instead, it gives me numbers like this: 101110121011101310111012101110149.2233720368548E+189.2233720368548E+189.2233720368548E+18
As correctly pointed out in one of the comments, for your specific case you can use fseek ( $file, 0 ) right before writing, such as:
fseek ( $file, 0 );
fwrite($file, $output);
Or even simpler you can rewind($file) before writing, this will ensure that the next write happens at byte 0 - ie the start of the file.
The reason why the file gets appended it is because you're opening the file in append and truncate mode, that is "w+". You have to open it in readwrite mode in case you do not want to reset the contents, just "r+" on your fopen, such as:
fopen("downloadcount.txt", "r+")
Just make sure the file exists before writing!
Please see fopen modes here:
https://www.php.net/manual/en/function.fopen.php
And working code here:
https://bpaste.net/show/iasj
It will be much simpler to use file_get_contents/file_put_contents:
// update with more precise path to file:
$content = file_get_contents(__DIR__ . "/downloadcount.txt");
echo $content;
$output = (int) $content + 1;
// by default `file_put_contents` overwrites file content
file_put_contents(__DIR__ . "/downloadcount.txt", $output);
That appending should just be a typecasting problem, but I would not encourage you to handle counts the file way. In order to count the number of downloads for a file, it's better to make a database update of a row using transactions to handle concurrency properly, as doing it the file way could compromise accuracy.
You can get the content, check if the file has data. If not initialise to 0 and then just replace the content.
$fileContent = file_get_contents("downloadcount.txt");
$content = (!empty($fileContent) ? $fileContent : 0);
$content++;
file_put_contents('downloadcount.txt', $content);
Check $str or directly content inside the file

Warning: flock() expects parameter 1 to be resource, string given

I have this very simple program in PHP that does not want to work. It returns the same error as topic for unknown reasons to me.
$file = 'counter.txt';
$counter = file_get_contents($file);
if(flock($file, LOCK_EX)){
$counter += 1;
echo $counter;
$write = fopen($file, 'w') or die('Unable');
fwrite($write, $counter);
flock($file,LOCK_UN);
}
You have a few things out of order,
$file = 'counter.txt';
$counter = file_get_contents($file);
$write = fopen($file, 'w') or die('Unable'); //move this line
if(flock($write, LOCK_EX)){ //-- change this
$counter += 1;
echo $counter;
fwrite($write, $counter);
flock($write,LOCK_UN); //-- change this
}
The main problem is flock takes a (stream)resource as it's input, and the filename is just a string. So instead of $file you just need to use $write which is your file handle (resource), and then move fopen before the flock call.
If you are writing a single line do this instead
$file = 'counter.txt';
$counter += 1;
if(!file_put_contents($file, $counter, LOCK_EX)) or die('Unable');
http://php.net/manual/en/function.file-put-contents.php
It's pretty much equivalent to what you have there. Well except it's way shorter 3 vs 9 lines, easier, and Kooler.
I could even reduce this further down to 1 line:
if(!file_put_contents('counter.txt', ++$counter, LOCK_EX)) or die('Unable');
The LOCK_EX flag is Lock exclusive, basically the same thing as flock, just in this case PHP handles all the file stream stuff for you.
The real difference is if you do this in a loop, it's expensive getting file handles so to loop output into file_put_content is way less efficient then to open the file (outside the loop) and write to the same handle during inside a loop.
Hesse the reason I said this above.
If you are writing a single line do this instead
Hope that makes sense.
Based on laracasts
Solution was to give Apache group (www-data) read and write permissions to /storage/framework/cache applying recursively.

PHP using fopen to download txt file via URL. How to limit the amount to download?

I have written a php script which parses this text file
http://www.powerball.com/powerball/winnums-text.txt
Everything is good, but I wish to control the amount that is download i.e. I do not need every single result maybe max the first 5. At the moment I am downloading the entire file (which is a waste of memory / bandwidth).
I saw the fopen has a parameter which supposed to limit it but whatever value I placed in has no effect on the amount of text that is downloaded.
Can this be done? Thank you for reading.
Here is a small snippet of the code in question which is downloading the file.
<?php
$file = fopen("http://www.powerball.com/powerball/winnums-text.txt","rb");
$rows = array();
while(!feof($file))
{
$line = fgets($file);
$date = explode("Draw Date",$line);
array_push($rows,$date[0]);
}
fclose($file);
?>
Thanks everyone this is the code which just downloads the first row of results
while(!feof($file))
{
$line = fgets($file);
$date = explode("Draw Date",$line);
array_push($rows,$date[0]);
if(count($rows)>1)
{
break;
}
}
fclose($file);
You can break whenever you don't need more data. In this example when count($rows)>100
while(!feof($file)) {
$line = fgets($file);
$date = explode("Draw Date",$line);
array_push($rows,$date[0]);
if (count($rows)>100)
break;
}
The issue is that your while condition is only met once you've read through to the end of the file. If you only want to get the first N lines you'll need to change that condition. Something like this might help get you started:
$lineCountLimit = 5;
$currentLineCount = 0;
while($currentLineCount < $lineCountLimit)
{
$line = fgets($file);
$date = explode("Draw Date",$line);
array_push($rows,$date[0]);
$currentLineCount++;
}
Please try the following recipe to download only a part of the file, like 10 KBytes first, then split to lines and analyze them. How to partially download a remote file with cURL?

Append at the beginning of the file in PHP [duplicate]

This question already has answers here:
Need to write at beginning of file with PHP
(10 answers)
Closed 9 years ago.
Hi I want to append a row at the beginning of the file using php.
Lets say for example the file is containing the following contnet:
Hello Stack Overflow, you are really helping me a lot.
And now i Want to add a row on top of the repvious one like this:
www.stackoverflow.com
Hello Stack Overflow, you are really helping me a lot.
This is the code that I am having at the moment in a script.
$fp = fopen($file, 'a+') or die("can't open file");
$theOldData = fread($fp, filesize($file));
fclose($fp);
$fp = fopen($file, 'w+') or die("can't open file");
$toBeWriteToFile = $insertNewRow.$theOldData;
fwrite($fp, $toBeWriteToFile);
fclose($fp);
I want some optimal solution for it, as I am using it in a php script. Here are some solutions i found on here:
Need to write at beginning of file with PHP
which says the following to append at the beginning:
<?php
$file_data = "Stuff you want to add\n";
$file_data .= file_get_contents('database.txt');
file_put_contents('database.txt', $file_data);
?>
And other one here:
Using php, how to insert text without overwriting to the beginning of a text file
says the following:
$old_content = file_get_contents($file);
fwrite($file, $new_content."\n".$old_content);
So my final question is, which is the best method to use (I mean optimal) among all the above methods. Is there any better possibly than above?
Looking for your thoughts on this!!!.
function file_prepend ($string, $filename) {
$fileContent = file_get_contents ($filename);
file_put_contents ($filename, $string . "\n" . $fileContent);
}
usage :
file_prepend("couldn't connect to the database", 'database.logs');
My personal preference when writing to a file is to use file_put_contents
From the manual:
This function is identical to calling fopen(), fwrite() and fclose()
successively to write data to a file.
Because the function automatically handles those three functions for me I do not have to remember to close the resource after I'm done with it.
There is no really efficient way to write before the first line in a file. Both solutions mentioned in your questions create a new file from copying everything from the old one then write new data (and there is no much difference between the two methods).
If you are really after efficiency, ie avoiding the whole copy of the existing file, and you need to have the last inserted line being the first in the file, it all depends how you plan on using the file after it is created.
three files
Per you comment, you could create three files header, content and footer and output each of them in sequence ; that would avoid the copy even if header is created after content.
work reverse in one file
This method puts the file in memory (array).
Since you know you create the content before the header, always write lines in reverse order, footer, content, then header:
function write_reverse($lines, $file) { // $lines is an array
for($i=count($lines)-1 ; $i>=0 ; $i--) fwrite($file, $lines[$i]);
}
then you call write_reverse() first with footer, then content and finally header. Each time you want to add something at the beginning of the file, just write at the end...
Then to read the file for output
$lines = array();
while (($line = fgets($file)) !== false) $lines[] = $line;
// then print from last one
for ($i=count($lines)-1 ; $i>=0 ; $i--) echo $lines[$i];
Then there is another consideration: could you avoid using files at all - eg via PHP APC
You mean prepending. I suggest you read the line and replace it with next line without losing data.
<?php
$dataToBeAdded = "www.stackoverflow.com";
$file = "database.txt";
$handle = fopen($file, "r+");
$final_length = filesize($file) + strlen($dataToBeAdded );
$existingData = fread($handle, strlen($dataToBeAdded ));
rewind($handle);
$i = 1;
while (ftell($handle) < $final_length)
{
fwrite($handle, $dataToBeAdded );
$dataToBeAdded = $existingData ;
$existingData = fread($handle, strlen($dataToBeAdded ));
fseek($handle, $i * strlen($dataToBeAdded ));
$i++;
}
?>

PHP what is the best way to write data to middle of file without rewriting file

I am working with large text files in php (1GB+), I am using
file_get_contents("file.txt", NULL, NULL, 100000000,100);
To get data from the middle of the file, but if i wanted to change the data in the file to something that is of different change than the origional data, I would have to re-write the entire file.
How can I change data within the file (variable length) without overwriting data if the data is larger than the original? I keep an index of the different data blocks within the file and their byte location. It seems that the only alternative is to dedicate x amount of bytes to each piece of data and then rewrite that block if i wanted to change it... the problem with this is that it would take up a lot more space than needed in just null bytes, and it would take longer to write... and that still would not solve how to "remove" data, as the file could never shrink in size... I really need some help here...
If I used prefixed blocks for each piece of data in the file, like 1 mb, then I wanted to enter data that was only 100kb, that entry would take 10x actual needed space, and the entry could never be changed to something more than 1mb of data, as it would overwrite more than 1st dedicated block... removing it would not be possible... hope this makes any sense... I am not looking for alternatives, I am looking to write and change data in the middle of files, hehe...
UPDATE: Yes, I would like to replace the old data, but if the new data extends more than the old data I would want the rest of the data to be pushed further into the file...
consider this: 0000000HELLODATA00000000
the zeros represent empty space, nothing... now I would like to replace HELLO with SOMETHING, now since something is larger than hello, simply writing in the starting point of hello would extend byond hello and start overwriting data... therefore i would like DATA to be pushed futher into the file, to make room for SOMETHING without overwriting DATA... hehe
To Overwrite Data :
$fp = fopen("file.txt", "rw+");
fseek($fp, 100000000); // move to the position
fwrite($fp, $string, 100); // Overwrite the data in this position
fclose($fp);
To Inject Data
This is a tricky because you have to rewrite the file. It can be optimized with partial modificationfrom point of injection rather than the whole file
$string = "###INJECT THIS DATA ##### \n";
injectData("file.txt", $string, 100000000);
Function Used
function injectData($file, $data, $position) {
$fpFile = fopen($file, "rw+");
$fpTemp = fopen('php://temp', "rw+");
$len = stream_copy_to_stream($fpFile, $fpTemp); // make a copy
fseek($fpFile, $position); // move to the position
fseek($fpTemp, $position); // move to the position
fwrite($fpFile, $data); // Add the data
stream_copy_to_stream($fpTemp, $fpFile); // #Jack
fclose($fpFile); // close file
fclose($fpTemp); // close tmp
}
A variant on Baba's answer, not sure if it would be more efficient when working with larger files:
function injectData($file, $data, $position) {
$fpFile = fopen($file, "rw+");
$fpTemp = fopen('php://temp', "rw+");
stream_copy_to_stream($fpFile, $fpTemp, $position);
fwrite($fpTemp, $data);
stream_copy_to_stream($fpFile, $fpTemp, -1, $position);
rewind($fpFile);
rewind($fpTemp);
stream_copy_to_stream($fpTemp, $fpFile);
fclose($fpFile);
fclose($fpTemp);
}
injectData('testFile.txt', 'JKL', 3);
Variant of my earlier method that eliminates one of the stream_copy_to_stream() calls, so should be a shade faster:
function injectData3($file, $data, $position) {
$fpFile = fopen($file, "rw+");
$fpTemp = fopen('php://temp', "rw+");
stream_copy_to_stream($fpFile, $fpTemp, -1, $position);
fseek($fpFile, $position);
fwrite($fpFile, $data);
rewind($fpTemp);
stream_copy_to_stream($fpTemp, $fpFile);
fclose($fpFile);
fclose($fpTemp);
}
Another variant of the injectData() function:
function injectData($file, $data, $position)
{
$temp = fopen('php://temp', "rw+");
$fd = fopen($file, 'r+b');
fseek($fd, $position);
stream_copy_to_stream($fd, $temp); // copy end
fseek($fd, $position); // seek back
fwrite($fd, $data); // write data
rewind($temp);
stream_copy_to_stream($temp, $fd); // stich end on again
fclose($temp);
fclose($fd);
}
It copies the end of file (from $position onwards) into a temporary file, seeks back to write the data and stitches everything back up.

Categories