开发者

How do I perform multiple replacements with Perl?

I have Perl code:

my $s =  "The+quick+brown+fox+jumps+over+the+lazy+dog+that+is+my+dog";

I want to replace every + with space and dog with cat.

I have this regular expression开发者_开发问答:

$s =~ s/\+(.*)dog/ ${1}cat/g;

But, it only matches the first occurrence of + and last dog.


You can use the 'e' modifier to execute code in the second part of an s/// expression.

$s =~ s/(\+)|(dog)/$1 ? ' ' : 'cat'/eg;

If $1 is true, that means the \+ matched, so it substitutes a space; otherwise it substitutes "cat".


Perl 5.14 and newer has the ability to chain substitutions with a non-destructive assignment so you can kill 3 birds with one stone: do your two global substitutions plus assign the result to a new variable without modifying your original variable.

my $s =  "The+quick+brown+fox+jumps+over+the+lazy+dog+that+is+my+dog";
my $result = $s =~ s/+/ /gr 
                =~ s/dog/cat/gr; 

Will replace all your + with space and replace every dog with cat, assigning the result into a new variable. In a one-liner.


Two regular expressions might make your life a lot easier:

$s =~ s/\+/ /g;
$s =~ s/dog/cat/g;

The following matches "+," followed by a bunch of stuff, followed by "dog." Also, "+" is technically a metacharacter.

/+(.*)dog/


A hash may do what you want:

#!/usr/bin/perl

use strict;
use warnings;

my $s =  "The+quick+brown+fox+jumps+over+the+lazy+dog+that+is+my+dog";

my %replace = (
    "+" => " ",
    dog => "cat",
);

$s =~ s/([+]|dog)/$replace{$1}/g;

print "$s\n";

In the comments I see that you are concerned with performance, the two regex solution is more performant. This is because any solution that works for one regex will need to use captures (which slow down the regex).

Here are the results of a benchmark:

eval: The quick brown fox jumps over the lazy cat that is my cat
hash: The quick brown fox jumps over the lazy cat that is my cat
two: The quick brown fox jumps over the lazy cat that is my cat
         Rate hash eval  two
hash  33184/s   -- -29% -80%
eval  46419/s  40%   -- -72%
two  165414/s 398% 256%   --

I used the following benchmark:

#!/usr/bin/perl

use strict;
use warnings;

use Benchmark;

my $s =  "The+quick+brown+fox+jumps+over+the+lazy+dog+that+is+my+dog";

my %replace = (
    "+" => " ",
    dog => "cat",
);

my %subs = (
    hash => sub {
        (my $t = $s) =~ s/([+]|dog)/$replace{$1}/g;
        return $t;
    },
    two => sub {
        (my $t = $s) =~ s/[+]/ /g;
        $t =~ s/dog/cat/g;
        return $t;
    },
    eval => sub {
        (my $t = $s) =~ s/(\+)|(dog)/$1 ? ' ' : 'cat'/eg;
        return $t;
    },
);

for my $k (sort keys %subs) {
    print "$k: ", $subs{$k}(), "\n";
}

Benchmark::cmpthese -1, \%subs;


Simple answer - use 2 lines!:

$s =~ s/+/ /g;
$s =~ s/dog/cat/g;

It could probably be done in one line with 'non-greedy' matching, but this should do the trick


If speed is important, you should probably stick with two lines. But when I need to do multiple substitions at once I usually care more about convenience, so I use a hash like suggested by Chas. Owens. Two advantages over the two-liner being that it's easy to modify, and it behaves like expected (e.g. when substituting "cat" for "dog" and "dog" for "cat" at the same time).

However, I am much to lazy to write the regex by hand and prefer to assemble it with join, and use map to escape stuff:

#!/usr/bin/perl

use strict;
use warnings;

my $s = "The+quick+brown+fox+jumps+over+the+lazy+dog+that+is+my+dog";

my %replace = (
    "+" => " ",
    dog => "cat",
);

my $regex = join "|", 
    #use quotemeta to escape special characters
    map  { quotemeta } 
    #reverse sort the keys because "ab" =~ /(a|ab)/ returns "a"
    sort { $b cmp $a } keys %replace;

#compiling the regex before using it prevents
#you from having to recompile it each time
$regex = qr/$regex/;

$s =~ s/($regex)/$replace{$1}/g;

print "$s\n";


I know this is an old thread, but here's a one-liner for Perls earlier than v5.14:

my $s = 'The+quick+brown+fox+jumps+over+the+lazy+dog+that+is+my+dog';
$s = do {local $_ = $s; s/\+/ /g; s/dog/cat/g; $_};
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜