2

EDIT: For anyone else looking for an answer, I used this. Probably the fastest solution to the problem.


Suppose I have a very large test.txt file containing:

line1
line4
line5
line6

I wish to add line2 and line3 after line1. I use the following code to achieve this:

$file = fopen('test.txt', 'r+');
fseek($file, 5);
fwrite($file, "\r\nline2\r\nline3\r\n");

However, the txt file now becomes, with line4 and line5 being overwritten:

line1
line2
line3
line6

I get the same result with c+ mode in fopen. I can't use a or a+ as they just append content to the end of the file. I also can't read the whole file into a string and analyse it to make changes, as this file is really huge.

Any way to fix this problem?

Note: Any solutions that involve reading the whole file as a string and then making changes from there will not be feasible due to the large amount of content in the file. If there are any cheeky workarounds to add content to a few lines between the first and second line, it will be good enough. :D

Community
  • 1
  • 1
chris97ong
  • 6,870
  • 7
  • 32
  • 52
  • You can't simply add new content into the middle of a file; filesystems don't shuffle everything up as you insert new characters, you actually have to write code to do that yourself.... generally opening 1 file for reading, 1 for writing and then renaming at the end – Mark Baker Sep 08 '16 at 13:39
  • 1
    Generally you can [stream copy](http://www.php.net/manual/en/function.stream-copy-to-stream.php) from the first to second file up to your seek position, then add your new content manually to the second file, then stream copy the remaining content from the seek position to end of file – Mark Baker Sep 08 '16 at 13:40
  • you could add enough white space to give you room to insert 2 lines, then you copy the line there, and reinsert it and another line. This isn't ideal though because every line would need the spaces, and it would degrade as you add/overwrite lines ( you could only add as much data in as you had left empty space). It would also bloat the file size needlessly. The basic problem is that each byte ( character )has an position in the file, like pegs on a grid. To seek just moves the pointer to a grid cell and then writes from there, overwriting the existing pegs in the grid cells. – ArtisticPhoenix Sep 08 '16 at 14:18
  • Only pure php? If file realy large - try `exec` with: ` gawk -i inplace -v n=line_number -v s="content" 'NR == n {print s} {print}' path_to_file` – Alex Muravyov Sep 08 '16 at 14:44

2 Answers2

1

Sadly, fopen and friends do not allow inserts, only appends. From their perspective, the file is a large, growable array. Therefore, there is no call to insert data, only to overwrite.

However, you can get around this in several ways. One easy way is to make a temporary file opened with a, fwrite line 1 into it from the original file, append lines 2 and 3, then write the rest of the original file into the temporary file, in chunks. Once you've transferred the whole file (without loading the whole thing into memory at once, notice), then you can move the temporary file on top of the original one.

fmt
  • 993
  • 9
  • 18
0

You can do this by creating an array from each line. Then replace searched line with whatever you want.

$searchLine = 'line1'; 
$file = getcwd()."test.txt";
$lineArray = file( $file , FILE_IGNORE_NEW_LINES ); 
$lineArray[$searchLine] =  $searchLine."\r\nline2\r\nline3\r\n"; // Replace the line with new ones
file_put_contents( $filename , implode( PHP_EOL, $lineArray) ); // Put the content
mertizci
  • 538
  • 3
  • 12
  • Problem is that the file is very very big, so this may not be feasible. – chris97ong Sep 08 '16 at 13:51
  • @chris97ong What about replacing those lines with preg_replace ? I dont know regex is suitable for this, but just an idea. – mertizci Sep 08 '16 at 13:54
  • @mertizici Any solution that will require reading the whole file's content is not very feasible :/ – chris97ong Sep 08 '16 at 13:56
  • @chris97ong - using any of the file stream functions can handle files as large as the max integer ( 32 bit or 64bit ). Streaming like `fread` reads only a small part of the file at a time and then reuses the memory. It may take time to read a large file, but I've managed to read files in excess of 10 million lines, and gigabyte sizes. I think (2GB) is the limit on 32 bit systems, if I recall correctly. – ArtisticPhoenix Sep 08 '16 at 14:25