Axlex has asked for the wisdom of the Perl Monks concerning the following question:

Hey wise monks,

as the title mentions, i'm trying to replace multiple strings in a string. i have 2 arrays one with the search strings and one with the replace strings. Now i wanna loop throug all search strings, but it doesnt work at all. :(

my example code so far:
use strict; use warnings; my $a = "aRep"; my $b = "bRep"; my $c = "cRep"; my @Search1 = ("?a?","?b?","?c?"); my @Replace = ($a,$b,$c); my $i=0; my $string = '?a? ?b? ?c?'; print "old string: $string1\n"; for (;$i<$#Search+1;$i++) { $string =~ s/"$Search[$i]"/"$Replace[$i]"/g; }
I would be grateful for any suggestions.

Replies are listed 'Best First'.
Re: Replace multiple strings in a string
by choroba (Cardinal) on May 07, 2014 at 15:10 UTC
    You are almost there.
    1. Remove the double quotes in the regular expression. They have no special meaning, so Perl tries to search for double quotes in the string - but there are none.
    2. ? has a special meaning in a regex. To be able to match it literally, you have to escape it. To escape the content of a variable, use the \Q.
    3. Your code as shown doesn't compile. There are some misnamed variables.

    Here is the fixed version:

    لսႽ† ᥲᥒ⚪⟊Ⴙᘓᖇ Ꮅᘓᖇ⎱ Ⴙᥲ𝇋ƙᘓᖇ

      Thank You Choroba for your fast Reply.

      It didn't compile because i tried several ways and copied one, changing the variables, but obviously not all.

      I already tried escaping, but in the wrong way it seems. ( "\?" and single quoted '?')

      Big Thx :D

Re: Replace multiple strings in a string
by InfiniteSilence (Curate) on May 07, 2014 at 15:51 UTC

    Although you already have your answer for the given code I should point out the inefficiency of repeatedly running regexes in that fashion:

    use strict; use warnings; use Benchmark qw|timethese cmpthese|; my $a = "aRep"; my $b = "bRep"; my $c = "cRep"; my @Search = ("?a?", "?b?", "?c?"); my @Search1 = (qr|\?a\?|,qr|\?b\?|,qr|\?c\?|); my @Replace = ($a,$b,$c); my $i=0; my $string = '?a? ?b? ?c?'; print "old string: $string\n"; print qq~ one --- \n~; &std_replace; print qq|String is now $string\n|; print qq~ two --- \n~; &improved_replace; print qq|String is now $string\n|; print qq~ Comparison --- \n~; timethese (100_000, {'STD_REPLACE'=>\&std_replace, 'IMP_REPLACE'=>\&improved_replace}); sub std_replace { $string = '?a? ?b? ?c?'; for (my $i = 0; $i < $#Search + 1; $i++) { $string =~ s/\Q$Search[$i]/$Replace[$i]/g; } } sub improved_replace { $string = '?a? ?b? ?c?'; for (;$i<$#Search1+1;$i++) { $string =~ s/$Search1[$i]/"$Replace[$i]"/g; } } 1;
    Produces,
    old string: ?a? ?b? ?c? one --- String is now aRep bRep cRep two --- String is now "aRep" "bRep" "cRep" Comparison --- Benchmark: timing 100000 iterations of IMP_REPLACE, STD_REPLACE... IMP_REPLACE: 0 wallclock secs ( 0.10 usr + 0.00 sys = 0.10 CPU) @ 1 +000000.00/s (n=100000) (warning: too few iterations for a reliable count) STD_REPLACE: 6 wallclock secs ( 4.99 usr + 0.00 sys = 4.99 CPU) @ 2 +0040.08/s (n=100000)

    But I suppose this only matters when the number of comparisons increases significantly.

    Celebrate Intellectual Diversity

Re: Replace multiple strings in a string
by Laurent_R (Canon) on May 07, 2014 at 17:50 UTC
    As a side note, $a and $b are special variable names used for sorting (and a few other things), it might be better not to use them for this. They won't prevent your simple program here from working, but it might break something somewhere else in a larger program. Also, rather than the C-style loop:
    for (;$i<$#Search+1;$i++) {
    it is slightly simpler, a bit more "perlish" and probably somewhat faster to use:
    for my $i (0..$#Search) {

      "faster" in this sort of context, even if true, is altogether irrelevant as any difference would be completely swamped by the time taken for the body of the loop. However the difference between:

      for (;$i<$#Search+1;$i++) {

      (which implies a continuation of a previous loop because of the missing $i initialisation btw) and

      for my $i (0 .. $#Search) {

      which is clear and succinct is absolutely compelling. Note in particular (you may have missed it because of the lack of white space) the hoop the C style for loop goes through to get the range correct? In the Perl style loop there are no hoops so the interpretation by the code author, the code maintainer and the Perl interpreter all align perfectly so the chance of the iteration range being wrong is pretty close to 0.

      Clarity and thus maintainability is the key reason for preferring the Perl style loop almost always.

      Perl is the programming world's equivalent of English
        Clarity and thus maintainability is the key reason for preferring the Perl style loop almost always.

        ++. I absolutely agree. I mentioned speed only as side effect and heavily qualified the claim that it is faster ("probably somewhat faster"). I agree that speed is probably not really relevant, let's say that it is good enough to know at least that it is not slower. Clarity is the most important reason, and although TIMTOWTDI, I am almost always using the Perl-style for loop for that reason (and also avoiding to use the $i array index when possible).

        Update: I just saw your other post below suggesting the use of a hash or an array of arrays (or array of hashes or similar data structures) and thus getting rid of the need to use array subscripts, I was actually also thinking of something similar, and I agree that this is a much better solution. The idea of walking through two arrays in parallel relying on a common index is at best clumsy, in view of the efficient data structures (in terms of coding simplicity) offered by Perl.

Re: Replace multiple strings in a string
by BillKSmith (Monsignor) on May 07, 2014 at 20:36 UTC
    Do them all at once.
    use strict; use warnings; my %replacement = ( '?a?' => 'aRep', '?b?' => 'bRep', '?c?' => 'cRep', ); my $regex = join '|', keys %replacement; $regex =~ s/\?/\\\?/g; my $string = '?a? ?b? ?c?'; print "old string: $string\n"; $string =~ s/($regex)/$replacement{$1}/ge; print "new string: $string\n";
    Bill
Re: Replace multiple strings in a string
by GrandFather (Saint) on May 08, 2014 at 11:12 UTC

    Focussing just on the "parallel" loops pattern (others have addressed why this is a poor solution to the actual problem), lets look at better solutions to managing related data. There are two common options in Perl for managing small amounts of related data depending on whether it is keyed or ordered.

    If the data is keyed and the order doesn't matter then use a hash:

    my %subs = ( '\?a\?' => 'aRep', '\?b\?' => 'bRep', '\?c\?' => 'cRep', ); for my $match (keys %subs) { $string =~ s/$key/$subs{Skey}/g; }

    If the order is important then use an array:

    my @subs = ( ['\?a\?' => 'aRep'], ['\?b\?' => 'bRep'], ['\?c\?' => 'cRep'], ); for my $match (@subs) { $string =~ s/$match->[0]/$match->[1]/g; }

    In either case you no longer have to deal with indexing through parallel arrays so the loop code is clearer and there is no way you can get your parallel data out of sync.

    Perl is the programming world's equivalent of English