I am trying to delete last line from my text file in PHP using ftruncate, but the problem is the length if line will keep varying most of the times. And I dont want to use file_put_contents, so that I write the file again because the file could be in Megabytes.
Please any suggestions ?
To get you an idea what I meant something like this:
$fp = fopen('file.txt', 'r+');
$pos = filesize('file.txt');
while ($pos > 0) {
$pos = max($pos - 1024, 0);
fseek($fp, $pos);
$tmp = fread($fp, 1024);
$tmppos = strrpos($tmp, "\n");
if ($tmppos !== false) {
ftruncate($fp, $pos + $tmppos);
break;
}
}
It will read the last 1024 bytes. Find the last newline in that buffer if it exists. If it exists truncate to that position. If is doesn't exists it reads the next 1024 bytes and check them. And so on.
Related
I'm trying to delete one line from CSV file by its line number, which I get as a parameter in URL.
I saw some discussions here, but it was mainly "delete a line by its id stored in first column" and so on. I tried to make it in the same way as others in these discussions, but it does not work. I only changed the condition.
if (isset($_GET['remove']))
{
$RowNo = $_GET['remove']; //getting row number
$row = 1;
if (($handle = fopen($FileName, "w+")) !== FALSE)
{
while (($data = fgetcsv($handle, 1000, ";")) !== FALSE)
{
//Here, I don't understand, why this condition does not work.
if ($row != $RowNo)
{
fputcsv($handle, $data, ';');
}
$row++;
}
fclose($handle);
}
}
I supposed, that it should work for me too, BCS just condition was changed. But it does not. It clears the whole file. Could you help me with it, please?
Thank you very much for any advice. Daniel.
You could load the file as an array of lines by using file().
Then remove the line and write the file back.
// read the file into an array
$fileAsArray = file($FileName);
// the line to delete is the line number minus 1, because arrays begin at zero
$lineToDelete = $_GET['remove'] - 1;
// check if the line to delete is greater than the length of the file
if ($lineToDelete > sizeof($fileAsArray)) {
throw new Exception("Given line number was not found in file.");
}
//remove the line
unset($fileAsArray[$lineToDelete]);
// open the file for reading
if (!is_writable($fileName) || !$fp = fopen($fileName, 'w+')) {
// print an error
throw new Exception("Cannot open file ($fileName)");
}
// if $fp is valid
if ($fp) {
// write the array to the file
foreach ($fileAsArray as $line) {
fwrite($fp, $line);
}
// close the file
fclose($fp);
}
If you have a unix system you could also use sed command:
exec("sed -e '{$lineToDelete}d' {$FileName}");
Remember cleaning command parameters if user input used:
https://www.php.net/manual/de/function.escapeshellcmd.php
Option if your CSV can fit to memory:
// Read CSV to memory array
$lines = file($fileName, FILE_SKIP_EMPTY_LINES | FILE_IGNORE_NEW_LINES);
// Remove element from array
unset($lines[$rowNo - 1]); // Validate that element exists!
// Rewrite your CSV file
$handle = fopen($fileName, "w+");
for ($i = 0; $i < count($lines); $i++) {
fputcsv($handle, $data, ';');
}
fclose($handle);
Option if your CSV can not fit to memory:
Use code from question, just write to separate file and later replace it with actual file:
$handle = fopen($FileName, "r");
// Read file wile not End-Of-File
while (!feof($fn)) {
if ($row != $RowNo) {
file_put_contents($FileName . '.tmp', fgets($fn), FILE_APPEND);
}
$row++;
}
fclose($handle);
// Remove old file and rename .tmp to previously removed file
unlink($FileName);
rename($FileName . '.tmp', $FileName);
Say I'm uploading a chunked file and I have to recompose it. I know the total chunks and data from every iteration.
I founded code like this:
for ($i = 1; $i <= $num_chunks; $i++) {
$file = fopen($target_file.$i, 'rb');
$buff = fread($file, 2097152);
fclose($file);
$final = fopen($target_file, 'ab');
$write = fwrite($final, $buff);
fclose($final);
unlink($target_file.$i);
}
Apparently, the 2097152 value, has no meaning, at least to me. I read the php docs but couldn't understand too much. Could anyone explain me how I should choose that secon param of fread? And how the thing works?
The second parameter is the amount of data to read, as your reading this in one chunk you have to be sure that it is enough to process any chunk. The value you've set is 2MB, which may be enough, but you could change the code so that it reads it in smaller chunks and loops till the input is fully read.
I've also changed it to open the output file once and just write the contents as you go along...
$final = fopen($target_file, 'wb'); // Open for write and start from beginning of file
for ($i = 1; $i <= $num_chunks; $i++) {
$file = fopen($target_file.$i, 'rb');
while($buff = fread($file, 4096)) {
fwrite($final, $buff);
}
fclose($file);
unlink($target_file.$i);
}
fclose($final);
I have got a large file in PHP of which I would like to replace the first 512 bytes with some other 512 bytes. Is there any PHP function that helps me with that?
If you want to optionally create a file and read and write to it (without truncating it), you need to open the file with the fopen() function in 'c+' mode:
$handle = fopen($filename, 'c+');
PHP then has the stream_get_contents() function which allows to read a chunk of bytes with a specific length (and from a specific offset in the file) into a string variable:
$buffer = stream_get_contents($handle, $length = 512, $offset = 0);
However, there is no stream_put_contents() function to write the string buffer back to the stream at a specific position/offset. A related function is file_put_contents() but it does not allow to write to a file-handle resource at a specific offset. But there is fseek() and fwrite() to do that:
$bytes_written = false;
if (0 === fseek($handle, $offset)) {
$bytes_written = fwrite($handle, $buffer, $length);
}
Here is the full picture:
$handle = fopen($filename, 'c+');
$buffer = stream_get_contents($handle, $length = 512, $offset = 0);
// ... change $buffer ...
$bytes_written = false;
if (0 === fseek($handle, $offset)) {
$bytes_written = fwrite($handle, $buffer, $length);
}
fclose($handle);
If the length of $buffer is not fixed this will not properly work. In that case it's better to work with two files and to use stream_copy_to_stream() as outlined in How to update csv column names with database table header or if the file is not large it is also possible to do that in memory:
$buffer = file_get_contents($filename);
// ... change $buffer ...
file_put_contents($filename, $buffer);
In PHP if you write to a file it will write end of that existing file.
How do we prepend a file to write in the beginning of that file?
I have tried rewind($handle) function but seems overwriting if current content is larger than existing.
Any Ideas?
$prepend = 'prepend me please';
$file = '/path/to/file';
$fileContents = file_get_contents($file);
file_put_contents($file, $prepend . $fileContents);
The file_get_contents solution is inefficient for large files. This solution may take longer, depending on the amount of data that needs to be prepended (more is actually better), but it won't eat up memory.
<?php
$cache_new = "Prepend this"; // this gets prepended
$file = "file.dat"; // the file to which $cache_new gets prepended
$handle = fopen($file, "r+");
$len = strlen($cache_new);
$final_len = filesize($file) + $len;
$cache_old = fread($handle, $len);
rewind($handle);
$i = 1;
while (ftell($handle) < $final_len) {
fwrite($handle, $cache_new);
$cache_new = $cache_old;
$cache_old = fread($handle, $len);
fseek($handle, $i * $len);
$i++;
}
?>
$filename = "log.txt";
$file_to_read = #fopen($filename, "r");
$old_text = #fread($file_to_read, 1024); // max 1024
#fclose(file_to_read);
$file_to_write = fopen($filename, "w");
fwrite($file_to_write, "new text".$old_text);
Another (rough) suggestion:
$tempFile = tempnam('/tmp/dir');
$fhandle = fopen($tempFile, 'w');
fwrite($fhandle, 'string to prepend');
$oldFhandle = fopen('/path/to/file', 'r');
while (($buffer = fread($oldFhandle, 10000)) !== false) {
fwrite($fhandle, $buffer);
}
fclose($fhandle);
fclose($oldFhandle);
rename($tempFile, '/path/to/file');
This has the drawback of using a temporary file, but is otherwise pretty efficient.
When using fopen() you can set the mode to set the pointer (ie. the begginng or end.
$afile = fopen("file.txt", "r+");
'r' Open for reading only; place
the file pointer at the beginning of
the file.
'r+' Open for reading and
writing; place the file pointer at the
beginning of the file.
$file = fopen('filepath.txt', 'r+') or die('Error');
$txt = "/n".$string;
fwrite($file, $txt);
fclose($file);
This will add a blank line in the text file, so next time you write to it you replace the blank line. with a blank line and your string.
This is the only and best trick.
How can i create in PHP a file with a given size (no matter the content)?
I have to create a file bigger than 1GB. Arround 4-10GB maximum
You can make use of fopen and fseek
define('SIZE',100); // size of the file to be created.
$fp = fopen('somefile.txt', 'w'); // open in write mode.
fseek($fp, SIZE-1,SEEK_CUR); // seek to SIZE-1
fwrite($fp,'a'); // write a dummy char at SIZE position
fclose($fp); // close the file.
On execution:
$ php a.php
$ wc somefile.txt
0 1 100 somefile.txt
$
If the content of the file is irrelevant then just pad it - but do make sure you don't generate a variable too large to hold in memory:
<?php
$fh = fopen("somefile", 'w');
$size = 1024 * 1024 * 10; // 10mb
$chunk = 1024;
while ($size > 0) {
fputs($fh, str_pad('', min($chunk,$size)));
$size -= $chunk;
}
fclose($fh);
If the file has to be readable by something else - then how you do it depends on the other thing which needs to read it.
C.
Late, but it's really easier than the other answers.
$size = 100;
$fp = fopen('foo.dat',"w+");
fwrite($fp,str_repeat(' ',$size),$size);
The w+ will either create the file or overwrite it if it already exists.
For a really big file I usually cheat:
`truncate -s 10g foo.dat`;
Look my code at bottom
^^ The best feature is here with 0s to create a 4GB file ^^
FUNCTION CreatFileDummy($file_name,$size) {
// 32bits 4 294 967 296 bytes MAX Size
$f = fopen($file_name, 'wb');
if($size >= 1000000000) {
$z = ($size / 1000000000);
if (is_float($z)) {
$z = round($z,0);
fseek($f, ( $size - ($z * 1000000000) -1 ), SEEK_END);
fwrite($f, "\0");
}
while(--$z > -1) {
fseek($f, 999999999, SEEK_END);
fwrite($f, "\0");
}
}
else {
fseek($f, $size - 1, SEEK_END);
fwrite($f, "\0");
}
fclose($f);
Return true;
}
test it ^^ Max in Php 32bit 4 294 967 296 :
CreatFileDummy('mydummyfile.iso',4294967296);
You want Write , Read and Creat File Dummy my code is here ^^ :
https://github.com/Darksynx/php
Algorithm to Generate a LARGE Dummy File