简体   繁体   中英

Perl file regex doesn't replace text

I would be surprised if this isn't a duplicate, but I cannot seem to find the solution anywhere for this problem. I am trying to replace all instances of a given string in a file with another string. The issue I'm having is that the script prints the replaced version but keeps the original. I'm very new to perl so I'm sure this is a trivial issue and I'm missing something

Code:

my $count;
my $fname = file_entered_by_user;
open (my $fhandle, '+<', $fname) or die "Could not open '$fname' for read: $!";

for (<$fhandle>) {
    $count += s/($item_old)/$item_new/g;
    print $fhandle $_;
}   
print "Replaced $count occurence(s) of '$item_old' with '$item_new'\n";
close $fhandle;

Original File:

This is test my test file where
I test the string perl script with test
strings. The word test appears alot in this file
because it is a test file.

Result File:

This is test my test file where
I test the string perl script with test
strings. The word test appears alot in this file
because it is a test file
This is sample my sample file where
I sample the string perl script with sample
strings. The word sample appears alot in this file
because it is a sample file.

Expected result file:

This is sample my sample file where
I sample the string perl script with sample
strings. The word sample appears alot in this file
because it is a sample file.

Additional info:

  • $item_old and $item_new are provided by the user. In the examples given I was replacing test with sample .
  • I am not interested in a one-liner solution to this problem. It is to be integrated with a larger program so one-liner solutions that can be run from the terminal won't be too helpful.

The problem is that you are using the +< mode, thinking it will do what you think it does. What you are doing is first reading all the lines in your file, putting the file handle position at end of file, then you are printing the lines after it.

This line

for (<$fhandle>) {

Reads all the lines of the file handle and puts them in a list, over which the loop then iterates. It reads until eof , and only after that your changes are added.

If you wanted to make your solution work, you would have to rewind the file handle before printing. Ie

seek($fhandle, 0, 0);

Though these kind of solutions are not very good, in my opinion. Especially when there is built-in functionality to handle exactly this kind of thing:

perl -pi.bak -we 's/$item_old/$item_new/g' yourfile.txt

The -i flag with the -p flag makes your code be applied to the text file, and alter it accordingly, saving a copy with the extension .bak . Of course, you have to supply the substitution you want to make, since you did not provide it.

Edit: I just saw that you don't want a one-liner. Well, to do what this one-liner does, you only need to open proper file handles and copy the changed file over the old. So, basically:

use strict;
use warnings;
use File::Copy;

open my $old, "<", $oldfile or die $!;
open my $new, ">", $newfile or die $!;

while (<$old>) {
    s/$item_old/$item_new/g
    print $new $_;
}
copy $newfile, $oldfile or die $!;

Most of the time, using the modes that allow both reading and writing on the same file handle are more complicated to use than it is worth, considering how easy it is to work with a copy of a file.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM