I am trying to remove a line from the middle of a large file. ( > 20MB). I know the position in the file of the start of the line to be removed.
Here is what I currently have.
/**
* Removes a line at a position from the file
* @param [int] $position The position at the start of the line to be removed
*/
public function removeLineAt($position)
{
$fp = fopen($this->filepath, "rw+");
fseek($fp, $position);
$nextLinePosition = $this->getNextLine($position, $fp);
$lengthRemoved = $position - $nextLinePosition;
$fpTemp = fopen('php://temp', "rw+");
// Copy the bottom half (starting at line below the line to be removed)
stream_copy_to_stream($fp, $fpTemp, -1, $nextLinePosition);
// Seek to the start of the line to be removed
fseek($fp, $position);
rewind($fpTemp);
// Copy the bottom half over the line to be removed
stream_copy_to_stream($fpTemp, $fp);
fclose($fpTemp);
fclose($fp);
}
However, while the code above does indeed remove the line from the file; since the temporary file is shorter than the original file. The tail end of the original file still exists and is doubled.
For Example: Original file was
The file after removing the line may look like
I have thought about somehow trimming the end of the main file by the $lengthRemoved amount however I can't think of an easy way to do that either.
Any suggestions?
For others who are looking for an answer here is the final function I came up with thanks to your help! Modify it to fit your needs.
/**
* Removes a line at a position from the file
* @param [int] $position The position at the start of the line to be removed
*/
public function removeLineAt($position)
{
$fp = fopen($this->filepath, "rw+");
fseek($fp, $position);
$nextLinePosition = $this->getNextLine($position, $fp);
$lengthRemoved = $position - $nextLinePosition;
$fpTemp = fopen('php://temp', "rw+");
// Copy the bottom half (starting at line below the line to be removed)
stream_copy_to_stream($fp, $fpTemp, -1, $nextLinePosition);
// Remove the difference
$newFileSize = ($this->totalBytesInFile($fp) + $lengthRemoved);
ftruncate($fp, $newFileSize);
// Seek to the start of the line to be removed
fseek($fp, $position);
rewind($fpTemp);
// Copy the bottom half over the line to be removed
stream_copy_to_stream($fpTemp, $fp);
fclose($fpTemp);
fclose($fp);
}
Since your file is very large you may want to use the sed
command via exec
if your php install will allow you to use that function.
exec("sed '3d' fileName.txt");
Where the 3
indicates the desired line number.
I think you are pretty close to a solution.
I would stick to your idea of removing the $lengthRemoved
from the end of the file and would suggest to use ftruncate($handle, $size);
before the fclose()
, where size is the size to truncate to (size = originalFilesize - lengthRemoved).
http://www.php.net/manual/en/function.ftruncate.php
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With