ashesh ashesh - 8 months ago 35
Perl Question

Search/replacement in string

I have a Perl program where the user enters some text, a search pattern and a replacement string.

I am using the the

operator to replace the search pattern by the replacement string, but in this scenario, if the user enters capture variables (like
) or backslash escapes (like
) 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?


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


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";