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

Hi all,

I am reading a template file line by line, substituting certain keywords with variables as I go and writing to an output file.

The keywords are identified in the template as <:KEYWORD:N:>, where 'N' is an optional multiplier that should be applied to the value of the variable associated with the KEYWORD.

Without the 'N' multiplier, this is easy; I simply do:
$line =~ s/<:KEYWORD:>/$value/;
However, I'm scratching my head over the best way to handle the multiplier.
I want to do something like:
$line =~ s/<:KEYWORD:(.*):>/$value * $1/;
where the replacement is evaluated on the fly.

How do I do this?

Thanks in advance,
--Darren

Replies are listed 'Best First'.
Re: Evaluating a regex replacement value
by prasadbabu (Prior) on Oct 05, 2005 at 11:59 UTC

    If i understood your question correctly, add e option modifier, to eavluate

    $line = '<:KEYWORD:4:>'; $value = 5; $line =~ s/<:KEYWORD:([^:>]*):?>/($1 != 0)? ($value * $1) : $value/e; print "$line";

    updated:

    Prasad

      duh!!
      *slaps forehead*
      I forgot all about the 'e' modifier... I really should pay closer attention to the doco.

      Thanks,
      --Darren
Re: Evaluating a regex replacement value
by Samy_rio (Vicar) on Oct 05, 2005 at 12:08 UTC

    Hai, Try this,

    use strict; my $value=5; while (my $line=<DATA>){ $line =~ s/<:KEYWORD:([^:>]*)\:?>/my $dum=$1; my $val; if ($dum) { $va +l = $value * $dum }else{ $val=$value} $val/e; print $line, "\n"; } __DATA__ <:KEYWORD:4:> <:KEYWORD:5:> <:KEYWORD:> <:KEYWORD:9:>

    Regards,
    Velusamy R.

      You can rewrite
      my $dum=$1; my $val; if ($dum) { $val = $value * $dum }else{ $val=$val +ue} $val
      To be ($1 != 0)? ($value * $1) : $value like prasadbabu had it, or really it can just be boiled down to:
      $value * ($1 || 1)
      (Assuming that $value is alway numeric) No need for a temp variable or the explicit if/else.

      also, since you're not chomp'ing $line, that "\n" in your print line is going to going to double-space the output...
        Thanks :)

        This is a nice, clean option and is what I've settled on.
        $line =~ s/<:KEYWORD:([^:>]*):?>/int($value * ($1 || 1))/e;
        The not chomp'ing was never an issue, as I wasn't explicity adding newlines to the output.

        heh... I've just been browsing through my copy of Perl Best Practices, and realised that I should abstract my KEYWORD<=>$values pairs into a lookup hash.... so I'm off to do that now :)

        Thanks again,
        --Darren
Re: Evaluating a regex replacement value
by davidrw (Prior) on Oct 05, 2005 at 13:11 UTC
    The above responses correctly suggest the /e modifier... though as i interperet OP's needs, "KEYWORD" needs to be used to get the value (i'll assume via a hash, but could be a function call or something else) ...
    use strict; use warnings; my %values = ( foo => 3, bar => 4, stuff => 5 ); while( <DATA> ){ s/<:(KEYWORD):(?:([^:>]+:)?>/ $values{$1} * ($2 || 1) /e; print; } __DATA__ <:foo:4:> <:bar:5:> <:bar:> <:stuff:9:> # OUTPUT: 12 20 4 45
    one last note to OP: if your intent was like above, but instead of a hash you have variables named $foo, $bar, and $stuff and those are what you want used, then it is possible to do eval('$'.$1), but not recommended -- it is probably better to rework the code to use a hash or function call to look of the value instead.
Re: Evaluating a regex replacement value
by Tanktalus (Canon) on Oct 05, 2005 at 16:53 UTC

    McDarren - I realise you've got most of this working already. You're saying that you're trying to abstract the keyword-lookup. I've done something very similar, and posted it here. In your case, you want to change the delimiters, but I think it can be used as a framework for a very good generic-lookup-and-then-modify tool. In fact, I use this code in production.

    Just create an expand_var function, and use that to do lookups.

Re: Evaluating a regex replacement value
by liverpole (Monsignor) on Oct 05, 2005 at 14:33 UTC
    Just want to point out that you can also using symbolic substitution, which was how I interpreted the OP's "substituting certain keywords with variables".   Although I made a recent argument in favor of always being in the habit of using strict, I can see why this might be a good situation for using "no strict 'refs'":
    #!/usr/bin/perl -w + use warnings; use strict; no strict 'refs'; + our $a = 10; our $b = 20; our $c = 33; our $d = 123; + my @lines = qw( <:a:0:> <:a:3:> <:b:0:> <:b:4:> <:c:5:> <:d:6:> ); + foreach my $line (@lines) { if ($line =~ s/<:(\w+):(\d+):>/$$1*($2||1)/e) { printf "Result = %d\n", $line; } }
    Output is:
        Result = 10
        Result = 30
        Result = 20
        Result = 80
        Result = 165
        Result = 738
    
      heh... "substituting certain keywords with variables"... was badly worded - what I should have said was: "substituting certain keywords with the values of associated variables"

      For example, I have stuff like:
      $line =~ s/<:INCOMING:([^:>]*):?>/int($incoming * ($1 || 1))/e; $line =~ s/<:OUTGOING:([^:>]*):?>/int($outgoing * ($1 || 1))/e; $line =~ s/<:INGRESS:>/$eth1/; $line =~ s/<:EGRESS:>/$eth0/; if ($line =~ /<:RADIUS:>/) { foreach my $ip (@radius_ips) { $thisline = $line; $thisline =~ s/<:RADIUS:>/$ip/; print OUT $thisline; } }
      (I'm in the process of abstracting the keyword/value pairs into a lookup hash, as per my previous post)