Search code examples
stringperlpattern-matching

I want to open a file and replace last occurrence pattern of string in perl


I have done this so far but its still not working.

open (GOOD_FILE,"<$pre.XXX.XXX$post") or die "Cannot open XXX";

# temporarily undefine the input line separator to read the entire file at once:
my $text = do { local $/; <GOOD_FILE> };
close GOOD_FILE or die $!;
#File name to replace XXXX.XXXXX.XXXX.XXXX.CCCC where last CCCC can be     anything 
$text =~ s/^(.*\n)?XXXX\.XXXX\.XXXX\.[^\n]*/$1myname/s;

open (GOOD_FILE,"<$pre.XXXX.XXXX$post") or die "Cannot open XXXX";
print GOOD_FILE $text;
close GOOD_FILE or die $!;

The input file looks like this:

I am tester testing
I am still learning.
XXXX.XXXX.XXXX.XXXX.CCCC
I am tester.
XXXX.XXXX.XXXX.XXXX.PPPP

It should produce this output:

I am tester testing
I am still learning.
XXXX.XXXX.XXXX.XXXX.CCCC
I am tester.
myname.

But I am getting empty file.

Thank you guys, I was able to solve the problem using the variable in what to replace I just regex as variable and it worked fine.


Solution

  • When you open a file for writing with the > mode, it's automatically truncated to zero length.

    It is possible to open a file for both reading and writing simultaneously, without truncating it, using the +< mode, but actually modifying a file like that is a bit tricky since it requires seeking.

    Instead, there are two simple ways to accomplish what you want. If you know the file will always be relatively short, you can just read it into memory, modify it and write it back:

    my $filename = 'filename.txt';
    
    open my $in, '<', $filename or die $!;
    # temporarily undefine the input line separator to read the entire file at once:
    my $text = do { local $/; <$in> };
    close $in or die $!;
    
    $text =~ s/^(.*\n)?MY\.FILE\.NAME\.[^\n]*/$1myname/s;
    
    open my $out, '>', $filename or die $!;
    print $out $text;
    close $out or die $!;
    

    The other solution, for large files, is to write the output to a temporary file and then move the temporary file over the original one when you're done. This is more or less what perl's -i switch does, and it allows you to edit arbitrarily long files line by line (as long as you have enough disk space to store the copy, anyway). Unfortunately, in your case matching the last occurrence of a particular string in the file gets a bit tricky using this method.

    (I suppose you could use File::ReadBackwards, but unfortunately there's no File::WriteBackwards module to go with it (and it would be quite difficult to write one due to limitations of the OS file I/O interface), so you'd basically have to write the output lines to the temporary file in reverse order and then do a second pass to copy them back into the original file in the correct order. It should be possible, but kind of complicated.)