Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Perl replace multiple strings simultaneously

Is there any way to replace multiple strings in a string? For example, I have the string hello world what a lovely day and I want to replace what and lovely with something else..

$sentence = "hello world what a lovely day";
@list = ("what", "lovely"); # strings to replace
@replist = ("its", "bad"); # strings to replace with
($val = $sentence) =~ "tr/@list/@replist/d";
print "$val\n"; # should print "hello world its a bad day"..

Any ideas why it's not working?

Thanks.

like image 553
Travv92 Avatar asked May 30 '13 02:05

Travv92


2 Answers

First of all, tr doesn't work that way; consult perldoc perlop for details, but tr does transliteration, and is very different from substitution.

For this purpose, a more correct way to replace would be

# $val
$val =~ s/what/its/g;
$val =~ s/lovely/bad/g;

Note that "simultaneous" change is rather more difficult, but we could do it, for example,

%replacements = ("what" => "its", "lovely" => "bad");
($val = $sentence) =~ s/(@{[join "|", keys %replacements]})/$replacements{$1}/g;

(Escaping may be necessary to replace strings with metacharacters, of course.)

This is still only simultaneous in a very loose sense of the term, but it does, for most purposes, act as if the substitutions are done in one pass.

Also, it is more correct to replace "what" with "it's", rather than "its".

like image 116
muhmuhten Avatar answered Nov 03 '22 16:11

muhmuhten


Well, mainly it's not working as tr///d has nothing to do with your request (tr/abc/12/d replaces a with 1, b with 2, and removes c). Also, by default arrays don't interpolate into regular expressions in a way that's useful for your task. Also, without something like a hash lookup or a subroutine call or other logic, you can't make decisions in the right-hand side of a s/// operation.

To answer the question in the title, you can perform multiple replaces simultaneously--er, in convenient succession--in this manner:

#! /usr/bin/env perl
use common::sense;

my $sentence = "hello world what a lovely day";

for ($sentence) {
  s/what/it's/;
  s/lovely/bad/
}

say $sentence;

To do something more like what you attempt here:

#! /usr/bin/env perl
use common::sense;

my $sentence = "hello world what a lovely day";

my %replace = (
  what => "it's",
  lovely => 'bad'
);

$sentence =~ s/(@{[join '|', map { quotemeta($_) } keys %replace]})/$replace{$1}/g;

say $sentence;

If you'll be doing a lot of such replacements, 'compile' the regex first:

my $matchkey = qr/@{[join '|', map { quotemeta($_) } keys %replace]}/;

...

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

EDIT:

And to expand on my remark about array interpolation, you can change $":

local $" = '|';
$sentence =~ s/(@{[keys %replace]})/$replace{$1}/g;
# --> $sentence =~ s/(what|lovely)/$replace{$1}/g;

Which doesn't improve things here, really, although it may if you already had the keys in an array:

local $" = '|';
$sentence =~ s/(@keys)/$replace{$1}/g;
like image 34
Julian Fondren Avatar answered Nov 03 '22 15:11

Julian Fondren