ashesh
ashesh

Reputation: 31

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

Answers (2)

melpomene
melpomene

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

Borodin
Borodin

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

Related Questions