helpdoc
helpdoc

Reputation: 1990

PHP : How to Replace some text from n th line to m th line from file?

I have a file like some.txt having content :

#start-first
Line 1
Line 2
Line 3
#end-first

#start-second
Line 1
Line 2
Line 3
Line 4
#end-second

#start-n
Line 1
Line 2
Line 3
Line 4
...
...
#end-n

I want to delete content from file from #start-second to #end-second or from #start-n to #end-n, actually #start-second is Start Marker for Second Text Block of file and #end-second is End Marker for Second Text Block of file.

How to delete content from Specific Start Block to same End block ?

Upvotes: 0

Views: 133

Answers (2)

ArtisticPhoenix
ArtisticPhoenix

Reputation: 21661

Here is my solution:

It's a bit more difficult to do it line by line, but it does let you manage memory better for large files, because your not opening the entire file all at once. Also you can replace multiple blocks a bit easier this way.

$file = 'test.txt';
//open file to read from
$f = fopen(__DIR__.DIRECTORY_SEPARATOR.$file,'r');
//open file to write to
$w = fopen(__DIR__.DIRECTORY_SEPARATOR.'out-'.$file,'w');

$state = 'start';  //start, middle, end

//start - write while looking for a start tag ( set to middle )
//middle - skip while looking for end tag  ( set to end )
//end - skip while empty ( set to start when not )

//Tags
$start = ['#start-second'];
$end = ['#end-second'];

 //read each line from the file
while( $line = fgets($f)){     
    if( $state == 'end' && !empty(trim($line))){
        //set to start on first non empty line after tag
        $state = 'start';
    }

    if( $state == 'start' ){
        if(in_array(trim($line),$start)){
            $state = 'middle';
        }else{
            fwrite($w, $line);
        }
    }else if( $state == 'middle' ){
        if(in_array(trim($line),$end)){
            $state = 'end';
        }
    }   
}
//close both files
fclose($f);
fclose($w);

//delete the input file
//unlink(__DIR__.DIRECTORY_SEPARATOR.$file);

//for debugging only
echo "<pre>";
echo file_get_contents(__DIR__.DIRECTORY_SEPARATOR.'out-'.$file)

And the output

#start-first
Line 1
Line 2
Line 3
#end-first

#start-n
Line 1
Line 2
Line 3
Line 4
...
...
#end-n

This will also accept an array of tags, so you can remove multiple chunks per go.

Most the PHP sandboxes ( or code sandboxes in general ) prevent you from using the functions, for security reasons. That said, we can emulate the body of the code, the parsing bit, to some extent. So that is what I have done here.

http://sandbox.onlinephpfunctions.com/code/0a746fb79041d30fcbddd5bcb00237fcdd8eea2f

That way you can try a few different tags and see how it works. For extra credit you could make this into a function that accepts the filepath and an array of open and start tags.

   /**
   * @var string  $pathName - full path to input file
   * @var string  $outputName - name of output file
   * @var array $tags - array of tags ex. ['start'=>['tag1'],'end'=>[...]]
   * @return string - path to output file
   */
   function($pathName, $outputName, array $tags){
        ....
   }

Upvotes: 0

Martijn
Martijn

Reputation: 16103

If these files are really big, there is a fairly lightweight solution:

$file = file_get_contents("example.txt");
// Find the start "#start-$block", "#end-$block" and the length between them:
$start = strpos($file, "#start-$block");
$end = strpos($file, "#end-$block");
$length = $end-$start+strlen("#end-$block");

$file = substr_replace($file, '', $start, length);
file_put_contents("example.txt", $file);

My original answer started with a regex:

$block = 4;

// Open the file
$file = openfile("example.txt");

// replace #start-$block, #end-$block, and everything inbetween with ''
$file = preg_replace("/#start\-".$block."(?:.*?)#end\-".$block."/s", '', $file);

// Save the changes
file_put_contents("example.txt", $file);

Regexes are expensive though, but sometimes easier to understand.

Upvotes: 2

Related Questions