52

How can I replace a particular line of text in file using php?

I don't know the line number. I want to replace a line containing a particular word.

pigrammer
  • 2,603
  • 1
  • 11
  • 24
kishore
  • 1,017
  • 3
  • 12
  • 21

12 Answers12

72

One approach that you can use on smaller files that can fit into your memory twice:

$data = file('myfile'); // reads an array of lines
function replace_a_line($data) {
   if (stristr($data, 'certain word')) {
     return "replacement line!\n";
   }
   return $data;
}
$data = array_map('replace_a_line', $data);
file_put_contents('myfile', $data);

A quick note, PHP > 5.3.0 supports lambda functions so you can remove the named function declaration and shorten the map to:

$data = array_map(function($data) {
  return stristr($data,'certain word') ? "replacement line\n" : $data;
}, $data);

You could theoretically make this a single (harder to follow) php statement:

file_put_contents('myfile', implode('', 
  array_map(function($data) {
    return stristr($data,'certain word') ? "replacement line\n" : $data;
  }, file('myfile'))
));

Another (less memory intensive) approach that you should use for larger files:

$reading = fopen('myfile', 'r');
$writing = fopen('myfile.tmp', 'w');

$replaced = false;

while (!feof($reading)) {
  $line = fgets($reading);
  if (stristr($line,'certain word')) {
    $line = "replacement line!\n";
    $replaced = true;
  }
  fputs($writing, $line);
}
fclose($reading); fclose($writing);
// might as well not overwrite the file if we didn't replace anything
if ($replaced) 
{
  rename('myfile.tmp', 'myfile');
} else {
  unlink('myfile.tmp');
}
Your Common Sense
  • 156,878
  • 40
  • 214
  • 345
gnarf
  • 105,192
  • 25
  • 127
  • 161
  • The less memory intensive approach is exactly what I needed (simple and easy to understand).....THANKS!!! –  Feb 06 '15 at 21:54
  • To avoid an error "access denied" code 5, for using `rename()` after `fclose()` too soon, I had to do `fseek()` to 0 for both handles, then use `stream_copy_to_stream()` to copy and then `fclose()` the handlers. – Leopoldo Sanczyk Jul 05 '19 at 07:39
  • The PHP manual explicitly states that `strstr()` should not be used to check the existence of strings in a string -- for that task, use the more performant strpos() function. – mickmackusa Jul 18 '22 at 12:30
8

You have to overwrite the entire file.

So, for the relatively small file, read file into array, search for the word, replace found row, write all the rest into file.

For the big file the algorithm is slightly different, but quite the same in general.

Important part is file locking

that's why we prefer a database.

Your Common Sense
  • 156,878
  • 40
  • 214
  • 345
3

If you don't know the line, you will have to search over all lines.

Either iterate over the file line by line or read the file into memory all at once. Then either find the word with a combination of strpos and str_replace or use preg_replace.

If you iterate, simply use strpos and replace the line once it didn't return FALSE. Then save the file back to disk.

Community
  • 1
  • 1
Gordon
  • 312,688
  • 75
  • 539
  • 559
3

You can also use multi-line mode with regular expressions

preg_match_all('/word}/m', $textfile, $matches);

this is, of course, assuming it's a smaller document at the ready and loaded. Otherwise, the other answers are far more 'real-world' of a solution.

Dan Heberden
  • 10,990
  • 3
  • 33
  • 29
  • This answer is super-misleading. It isn't actually replacing anything. What is that rogue `}` doing other than confusing researchers? That `m` flag is useless fir the given pattern -- it affects nothing. I do not understand the score that this answer has earned. This looks like a great candidate for the Disciplined badge. – mickmackusa Jul 18 '22 at 12:20
2
$filedata = file('filename');
$newdata = array();
$lookfor = 'replaceme';
$newtext = 'withme';

foreach ($filedata as $filerow) {
  if (strstr($filerow, $lookfor) !== false)
    $filerow = $newtext;
  $newdata[] = $filerow;
}

Now $newdata contains the file contents as an array (use implode() if you don't want array) with the line containing "replaceme" replaced with "withme".

kb.
  • 1,955
  • 16
  • 22
  • The PHP manual states that `strstr()` should not be used to check the existence of strings in a string -- for that task, use the more performant `strpos()` function. – mickmackusa Jul 18 '22 at 12:23
2

This is good if you are looking for a substring (ID) in a line and want to replace the old line with the new one.

Code:

$id = "123";
$new_line = "123,Programmer\r"; // We're not changing the ID, so ID 123 remains.
$contents = file_get_contents($dir);
$new_contents= "";
if( strpos($contents, $id) !== false) { // if file contains ID
    $contents_array = preg_split("/\\r\\n|\\r|\\n/", $contents);
    foreach ($contents_array as &$record) {    // for each line
        if (strpos($record, $id) !== false) { // if we have found the correct line
            $new_contents .= $new_line; // change record to new record
        }else{
            $new_contents .= $record . "\r";
        }
    }
    file_put_contents($dir, $new_contents); // save the records to the file
    echo json_encode("Successfully updated record!");
}
else{
    echo json_encode("failed - user ID ". $id ." doesn't exist!");
}

Example:

Old file:

ID,occupation

123,student

124,brick layer

Running the code will change file to:

New file:

ID,occupation

123,programmer

124,brick layer

Community
  • 1
  • 1
ChickenFeet
  • 2,653
  • 22
  • 26
1

You could turn the file into an array using the explode(); function, edit any item in the array, use the implode(); function to turn the array back into a string, then you can put the string back into the file using the file_put_contents(); function. This is shown in the following function :

function file_edit_contents($file_name, $line, $new_value){
  $file = explode("\n", rtrim(file_get_contents($file_name)));
  $file[$line] = $new_value;
  $file = implode("\n", $file);
  file_put_contents($file_name, $file);
}
DMVerfurth
  • 172
  • 1
  • 13
  • 1
    If you want to process a text file line by line, use `file()` instead of `explode(file_get_contents())`. More importantly, the OP clearly stated: "_I don't know the line number._" – mickmackusa Jul 18 '22 at 12:26
0

Maybe this could help:

$data = file("data.php");

for($i = 0;$i<count($data);$i++){
    echo "<form action='index.php' method='post'>";
    echo "<input type='text' value='$data[$i]' name='id[]'><br>";
}

echo "<input type='submit' value='simpan'>";
echo "</form>";

if(isset($_POST['id'])){
    file_put_contents('data.php',implode("\n",$_POST['id'])) ;
}
Calum
  • 5,308
  • 1
  • 22
  • 27
malik kurosaki
  • 1,747
  • 15
  • 9
0

This function should replace a full line in a file:

function replace($line, $file) {
    if ( file_get_contents($file) == $line ) {
        file_put_contents($file, '');
    } else if ( file($file)[0] == $line.PHP_EOL ) {
        file_put_contents($file, str_replace($line.PHP_EOL, '', file_get_contents($file)));
    } else {
        file_put_contents($file, str_replace(PHP_EOL.$line, '', file_get_contents($file)));
    }
}

The first if statement (line 2) checks if the line to remove is the only line. It then empties the file. The second if statement (line 4) checks if the line to remove is the first line in the file. If so, it then proceeds to remove that line by using str_replace($line.PHP_EOL, '', file_get_contents($file)). PHP_EOL is a new line, so this will remove the line contents and then the line break. Finally, the else statement will only get called if the line to remove is not the only contents and it's not at the start of the file. It then uses the str_replace, but this time with PHP_EOL.$line and not $line.PHP_EOL. That way, if the line is the very last line of the file, it will remove the line break before it and then delete the line.

Usage:

replace("message", "database.txt");

This removes a line with the contents message from the file database.txt if the line exists. If you wanted to shorten it, you could do something like this:

function replace($line,$file){if(file_get_contents($file)==$line){file_put_contents($file,'');}else if(file($file)[0]==$line.PHP_EOL){file_put_contents($file,str_replace($line.PHP_EOL,'', file_get_contents($file)));}else{file_put_contents($file,str_replace(PHP_EOL.$line,'',file_get_contents($file)));}}

I hope that answers your question :)

Tiernan Crotty
  • 187
  • 1
  • 10
  • There are two many file reads in this answer -- not only does this slow performance but it creates more opportunity for race condition conflicts. Calling `file()` -- which will fully populate an array from every line of text -- only so that the first line can be read is not optimal in terms of performance. Instead of smashing the code together to make it "shorter" (harder to read), write variable to hold values that are accessed more than once. DRY code is good code. – mickmackusa Jul 18 '22 at 12:17
0

If you not going to lock the file while processing it, then

  1. access the file,
  2. modify the line (if found) and stop looking for additional lines to replace,
  3. and re-save it with maximum haste (if found).

That said, it doesn't make sense to sacrifice accuracy for speed. This question states that a word must be matched in the line. For that reason, partial matches must be prevented -- regex offers word boundaries (\b).

$filename = 'test.txt';
$needle = 'word';

$newText = preg_replace(
    '/^.*\b' . $needle . '\b.*/mui',
    'whole new line',
    file_get_contents($filename)
    1,
    $count
);
if ($count) {
    file_put_contents($filename, $newText);
}

The pattern:

/     #starting pattern delimiter
^     #match start of a line (see m flag)
.*    #match zero or more of any non-newline character
\b    #match zero-width position separating word character and non-word character
word  #match literal string "word"
\b    #match zero-width position separating word character and non-word character
.*    #match zero or more of any non-newline character to end of line
/     #ending pattern delimiter
m     #flag tells ^ character to match the start of any line in the text
u     #flag tells regex engine to read text in multibyte modr
i     #flag tells regex engine to match letters insensitively

If using case-insensitive searching but you need the actual matched word in the replacement string, write parentheses around the needle in the pattern, then use $1 in your replacement string.

mickmackusa
  • 43,625
  • 12
  • 83
  • 136
-1

You can do like this:

$file = file('data.txt'); 
$data = 'new text';
$some_index = 2;
foreach($file as $index => $line){

   if($index == $some_index){
       $file[$index] = $data . "\n";
   }

}

$content = implode($file);
file_put_contents('data.txt', $content);
Vasic Danijel
  • 39
  • 1
  • 6
  • OP says: "_I don't know the line number._" This answer also destroys newline characters. And finally, this answer is missing its educational explanation. – mickmackusa Jul 18 '22 at 12:10
-1

I've had similar task and gnarf's answer helped a lot.

But better way to do this is JSON. If you have to change JSON file, you can use this code.

Code just gets existing JSON and applies that to the variable:

$base_data = json_decode(file_get_contents('data.json'), true);

$ret_data = json_encode($base_data , JSON_PRETTY_PRINT);

After this add/modify $ret_data array as you like and put it back to file:

file_put_contents('data.json', $ret_data)

`

$base_data = json_decode(file_get_contents('data.json'), true);
if(!empty($_POST["update_data_file"])){

    if(empty($_POST["update_key"]) || empty($_POST['update_value'])){

        return_response(false, true, "Update Key or Update Value is missing");
    }

    if(!is_array($_POST["update_key"])){

        return_response(false, false, "Update Key is not an array");

    }

    if(!is_array($_POST["update_value"])){

        return_response(false, false, "Update Key is not an array");
    }

    $update_keys = $_POST['update_key'];
    $update_values = $_POST['update_value'];

    $key_length = count($update_keys);

    $ret_data = $base_data; // $base_data is JSON from file that you want to update

    for($i=0; $i<$key_length; $i++){

        $ret_data[$update_keys[$i]] = $update_values[$i];

    }

    $ret_data = json_encode($ret_data, JSON_PRETTY_PRINT);
    if(file_put_contents('data.json', $ret_data)){

        return_response(true, false, "Data file updated");

    }

    return_response(false, false, "Error while updating data file");

}`

`

function return_response($success = true, $reload = false, $msg = ""){

    echo json_encode
    ( 
        [
            "success"=>$success,
            "msg"=> $msg,
            "reload"=> $reload
        ]
    );

    exit;

}`

JQuery part:

`

$("button").click(function(){
    let inputs_array = $(this).data("inputs").split(",");
    var input_value = "";

    var post_input_keys = [];
    var post_input_values = [];

    for(var i = 0; i < inputs_array.length; i++){
        
            input_value = $("#"+inputs_array[i]).val(); 
            post_input_keys[i] = inputs_array[i];
            post_input_values[i] = input_value;

        }

        send_ajax_data(post_input_keys, post_input_values);

    });`

`

function send_ajax_data(update_key = [], update_value = []){

    $.ajax({
        type : "POST",
        url : "path_to_php_file.php",
        data : {
            update_data_file: 1,
            update_key: update_key,
            update_value: update_value
        },
        dataType : 'json',
        success : function(data) {
            console.log(data.msg);
        }
    });

}`

HTML:

`

<form id="fill_details_form" class="form" action="" method="post">
    <input type="hidden" name="update_data_file" value="1" />

    <div class="row mt-3">
                <div class="col-sm-3">
                    <div class="form-group">
                        <label for="form_name">Logo Width</label>
                        <input type="text" class="form-control" id="logo_width" name="logo_width" value="" />
                        <div class="help-block with-errors"></div>
                    </div>
                </div>

                <div class="col-sm-3">
                    <div class="form-group">
                        <label for="form_name">Logo Height</label>
                        <input type="text" class="form-control" id="logo_height" name="logo_height" value="" />
                        <div class="help-block with-errors"></div>
                    </div>
                </div>

                <div class="col-sm-3">
                    <label for="form_name"></label>
                    <div class="form-group">
                        <button 
                            type="button" 
                            id="update_logo_dims" 
                            class="btn btn-primary"
                            data-inputs="logo_width,logo_height"
                        >
                            Update logo width and height
                        </button>
                    </div>
                </div>
            </div>
</form>

`

data.json example:

`

{
"logo_file_name": "favicon.png",
"logo_width": "153",
"logo_height": "36",
"logo_url": "https:\/\/google.com?logourl",
"website_url": "https:\/\/google.com",
"website_title": "WEbsite Title",
"favicon_file_name": "favicon.PNG",

}

`

temo
  • 612
  • 1
  • 9
  • 25