简体   繁体   中英

Modify a string in a file with perl

I have a file, d00.dat, and I need to change the string d01 for d02...With the following code it prints in the console the modificatons but they are not changed in the file d00.dat...how can I do that?

my $line = 0;
my $filename = "d00.dat";
open(my $fh, '<', $filename) || die "file could not open $! \n";
while( $line = <$fh>) {
if( $line =~ s/d01/d02/g ){
print "$line\n";
}

The simplest, fastest solution is read the file into memory, make the change, then write the changes back to the file.

my $qfn = "d00.dat";

my $file;
{
   open(my $fh, '<', $qfn)
      or die("Can't open \"$qfn\": $!\n");

   local $/;
   $file = <$fh>;
}

$file =~ s/d01/d02/g;

{
   open(my $fh, '>', $qfn)
      or die("Can't create \"$qfn\": $!\n");

   print($fh $file);
}

As a one-liner:

perl -i -pe's/d01/d02/g' d00.dat

If you are thinking about changing just those places, you can't -- have to replace the whole file.

You can print out each line, including the changes, to another file, then copy that over. Many tools work that way. Or, you can read in all lines, change them as suitable, and then overwrite the file.

my $filename = "d00.dat";

open(my $fh, '<', $filename) || die "file could not open $! \n";
my @new_lines = map { s/d01/d02/g; $_ } <$fh>;
close $fh;

open(my $fh_out, '>', $filename) || die "file could not open $! \n";
print $fh_out $_  for @new_lines;
close $fh_out;

Since open will close a filehandle if opened already you can also do

open my $fh, '<', $filename or die "Can't open $filename: $!";
my @new_lines = map { s/d01/d02/gr } <$fh>;

open $fh, '>', $filename or die "Can't open $filename: $!";
print $fh $_  for @new_lines;
close $fh;

where I dropped parens and changed to or , and added the $filename to the error message. I also use the /r modifier in regex (" non-destructive substitution "), with which the changed string is returned -- exactly as needed for that map , so now we don't need that lone $_ at the end of the block.

A newline at the end of die message, present in the question, has one subtle consequence: it suppresses information about the line number at which the die was emitted. While this is sometimes desired, in most cases it isn't and here we certainly want to see the line number of that call which failed! So I removed that additional newline in die 's message.


A file is a sequence of bytes. So if we want to replace a nope with yes ... what happens with the last character, e , once we replaced the first three? We have to move the rest of the file, following the original nope , to overwrite the e and so to now follow yes . So we need to overwrite the rest of the file, and in practice it's far easier to just replace the whole file.

If we wanted to replace the nope with yeah ... well, now it's four-for-four and we could in fact just overwrite those four characters. However, that's rarely the case and is more complicated to do.

While the question asks to replace d01 with d02 I'd imagine that this is an example while in practice the strings aren't of the same length in general. Even if they are ... replacing the file is much easier and safer, and most likely no efficiency loss will be noticed.

Perl has a switch to modify files in-place. I think that is what you are looking for:

perl -i.bak -p -e 's/d01/d02/g;' d00.dat

This will

  • rename d00.dat to d00.dat.bak
  • read d00.dat.bak
  • replace every occurence of d01 with d02
  • print the resulting line to d00.dat

Explanation:

  • -i edits files in-place and optionally makes a backup if some suffix is given. Eg -i.bak will create $file.bak , -i.before will create $file.before etc. Without a suffix (only -i ) there will be no backup so consider this dangerous.

  • -p places a loop like

     while(<>) { # your code print; } 

    around your code ( s/…/…/g; ) in this case

  • -e simply means "execute the following statement" .

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