How do I perform a Perl substitution on a string while keeping the original?

In Perl, what is a good way to perform a replacement on a string using a regular expression and store the value in a different variable, without changing the original?

I usually just copy the string to a new variable then bind it to the s/// regex that does the replacement on the new string, but I was wondering if there is a better way to do this?

$newstring = $oldstring;
$newstring =~ s/foo/bar/g;

Solution 1:

This is the idiom I've always used to get a modified copy of a string without changing the original:

(my $newstring = $oldstring) =~ s/foo/bar/g;

In perl 5.14.0 or later, you can use the new /r non-destructive substitution modifier:

my $newstring = $oldstring =~ s/foo/bar/gr; 

NOTE:
The above solutions work without g too. They also work with any other modifiers.

SEE ALSO:
perldoc perlrequick: Perl regular expressions quick start

Solution 2:

The statement:

(my $newstring = $oldstring) =~ s/foo/bar/g;

Which is equivalent to:

my $newstring = $oldstring;
$newstring =~ s/foo/bar/g;

Alternatively, as of Perl 5.13.2 you can use /r to do a non destructive substitution:

use 5.013;
#...
my $newstring = $oldstring =~ s/foo/bar/gr;

Solution 3:

Under use strict, say:

(my $new = $original) =~ s/foo/bar/;

instead.

Solution 4:

The one-liner solution is more useful as a shibboleth than good code; good Perl coders will know it and understand it, but it's much less transparent and readable than the two-line copy-and-modify couplet you're starting with.

In other words, a good way to do this is the way you're already doing it. Unnecessary concision at the cost of readability isn't a win.

Solution 5:

Another pre-5.14 solution: http://www.perlmonks.org/?node_id=346719 (see japhy's post)

As his approach uses map, it also works well for arrays, but requires cascading map to produce a temporary array (otherwise the original would be modified):

my @orig = ('this', 'this sucks', 'what is this?');
my @list = map { s/this/that/; $_ } map { $_ } @orig;
# @orig unmodified