Reputation: 31
I have a Perl program where the user enters some text, a search pattern and a replacement string.
I am using the the s///
operator to replace the search pattern by the replacement string, but in this scenario, if the user enters capture variables (like $1
) or backslash escapes (like \u
or \L
) in the replacement string, the replacement pattern should process these metacharacters but rather it is treating them as string characters.
I have the following code:
#!/usr/bin/perl -w
use strict;
chomp(my $text = <STDIN>); #read the text
chomp(my $regex = <STDIN>); #read the search pattern
chomp(my $replace = <STDIN>); #read the replacement pattern
$text=~s/$regex/$replace/g; # do replacement
print $text,"\n";
The sample input for this code is
fred flintstone and wilma flintstone are good couples
(fred|wilma) flintstone
\u\L$1\E Flintstone
The output is for above code is
\u\L$1\E Flintstone and \u\L$1\E Flintstone are good couples
I have found a way to make this correct in the following code:
#!/usr/bin/perl -w
use strict;
chomp(my $text = <STDIN>);
chomp(my $regex = <STDIN>);
chomp(my $replace = <STDIN>);
$replace = '"' . $replace . '"'; # creating the string as '"\u\L$1\E Flintstone"'
$text = ~s/$regex/$replace/gee; # applying double evaluation
print $text,"\n";
Now this code gives the correct output as
Fred Flintstone and Wilma Flintstone are good couples
I want to know if there is a better approach for this problem?
Upvotes: 2
Views: 94
Reputation: 85757
(First, you shouldn't use -w
anymore. It's been supplanted by the (lexically scoped, more predictable) use warnings
pragma in 2000.)
For your problem, you could use replace
from the Data::Munge module, which is "a clone of javascript's String.prototype.replace
".
use Data::Munge qw(replace);
$text = replace($text, $regex, $replace, 'g');
This will expand things like $&
or $1
, but not backslash sequences such as \u
. For that, you could specify your own expansion function, but then you'd have to parse and replace special sequences in your replacement string manually. For $1
and friends, this is easy, but something like \Ufoo\Q$1\Ebar\Ebaz
is hard to handle correctly, especially if $1
contains '\E'
(even perl itself has had problems in this area historically). But if you get that part working, it's easy to plug into replace
.
Upvotes: 4
Reputation: 126722
A double-quoted string cannot be interpolated without compiling it
The /ee
modifier carries the same warnings as eval
: it is always dangerous to execute code of any sort entered by the user, and even a simple string may contain a block consisting of any Perl code the user likes to enter, like "my dangerous string @{ unlink glob '*.*'}"
I suggest that you make use of the excellent String::Interpolate
module, which exports a safe_interpolate
function that will use the Safe
module to perform the interpolation in a "safe" compartment
#!/usr/bin/perl
use strict;
use warnings 'all';
use String::Interpolate 'safe_interpolate';
my $text = 'fred flintstone and wilma flintstone are good couples';
my $regex = '(fred|wilma) flintstone';
my $replace = '\u\L$1\E Flintstone';
$text =~ s/$regex/ safe_interpolate($replace) /eg; # do replacement
print $text,"\n";
Upvotes: 4