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

I just learned that this won't work because the back reference only works from the regex operator, and what I'm doing on the hash values down here is being taken literally.

My question is, what techniques can I look into in order to achieve the desired result?

Eventually, I would like to put the hash on an external text file with pattern on the left side, including grouping, a delimeter, and the replacement in the right side, including back references.

#!/usr/bin/perl use strict; my %fill = ( #'M(ary)' => 'G\1', 'M(ary)' => 'G$1', ); while (my $line = (<DATA>)) { foreach my $key (keys %fill) { if ($line =~ /$key/) { $line =~ s/$key/$fill{$key}/; } } print "$line"; } __DATA__ Mary has a little lamb

Replies are listed 'Best First'.
Re: backreference on hash
by AnomalousMonk (Archbishop) on Aug 13, 2015 at 22:40 UTC

    One way:

    c:\@Work\Perl\monks>perl -wMstrict -le "my %fill = ( '\bM(ary)\b' => 'G$1', '\bL(arry)\b' => 'B$1', ); ;; my $s = 'Mary has a little lamb, Larry has a big one'; print qq{'$s'}; ;; foreach my $key (keys %fill) { $s =~ s/$key/ qq{qq{$fill{$key}}} /gee; } print qq{'$s'}; " 'Mary has a little lamb, Larry has a big one' 'Gary has a little lamb, Barry has a big one'
    See also the discussions linked from the Re: Evaluating $1 construct in literal replacement expression node and also its root.


    Give a man a fish:  <%-(-(-(-<

Re: backreference on hash
by stevieb (Canon) on Aug 13, 2015 at 22:44 UTC

    You can get the effect you're after if you turn your hash into a dispatch table, then deref the sub to get its contents in the substitution side of the regex. This won't trigger undefined warnings for $1. The /e modifier treats the substitution side of the regex as perl code, which is why the sub executes. See perlretut for further information.

    #!/usr/bin/perl use strict; use warnings; my %fill = ( 'M(ary)' => sub {"G$1"}, ); while (my $line = (<DATA>)) { foreach my $key (keys %fill) { if ($line =~ /$key/) { $line =~ s/$key/&{$fill{$key}}/e; } } print "$line"; } __DATA__ Mary has a little lamb __END__ $ ./reghash.pl Gary has a little lamb

    I just realized that Storable won't save a sub, so this solution probably isn't what you want if you need to save the struct to a file.

    -stevieb

Re: backreference on hash
by Eily (Monsignor) on Aug 13, 2015 at 22:29 UTC

    Actually you could do what you want with the /e modifier (which evals the right hand side before replacement), though you'd need to double it (first for $fill{$key} to be interpreted, then $1). Be careful with that though, if the replacement comes from user input, it means users may execute any code from your perl program.

    s/$key/$fill{$key}/ee

Re: backreference on hash
by MidLifeXis (Monsignor) on Aug 14, 2015 at 13:24 UTC

    Careful about using a hash for this, as the keys are unordered order of the keys is undefined. It isn't wrong; it just has the potential for unexpected surprises if order of the replacements is important.

    Let's say you have a set of two regexes in your hash: { a => 'b', b => 'c'}. If the 'a' key is used first, you will end up with no 'b' values in your results. OTOH, if the 'b' key is used first, you might (if 'a' exists in the contents of $line).

    'abacus' => 'ccccus' # a => b, then b => c 'abacus' => 'bcbcus' # b => c, then a => b

    A 'better' solution for this might be an array of arrays (AoA), with the patterns in the order that you wish to apply them: ( [ a => 'b' ], [ b => 'c' ] ), and then stepping through the set of replacements in the expected order.

    --MidLifeXis