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

Hi All

I have a line like below, and I am trying to replace the tags with hash variables, but my current code will not replace two tags on the same line, it will only do the first one, I'm sure there is an easy solution and any help is appreciated.

%tag1% went to %tag2%

s/\%(\S+)\%/$template{$1}/gi;

Thanks

Replies are listed 'Best First'.
Re: s// problem
by lshatzer (Friar) on Apr 12, 2002 at 00:33 UTC
    Try making your quantifier, in this case +, non-greedy, with a ? after it. Look it up on perlre, search for 'greedy'.

    Updated:Look up Common Regex Gotchas in our Tutorials secion.

    Update 2: As loc and JayBonci mention, it is most likely not your regex, although I would still look into not making it greedy, but your hash might have a problem. Also there are quite a few templating systems out there, for HTML, etc... might be worth looking into, since you might run into lots of exceptions, and regexes, as powerful as they are, might not handle it (cleanly).

    Update 3: Again, helped by someone else, /i modifier might provide the wrong key, since hash keys, since they *are* case sensitive.
      But the "greedy" operator wouldn't be a problem in his case (not to say that it is a bad idea). If the greedy operator was a problem
      $str = "%tag1% went to %tag2% #with $str = ~ s/\%(\S+)\%/$template{$1}/gi;

      Would work just fine. However, the greedy operator syndrome would affect a test file such as:
      $str = "%tag1%ALONGSTRINGOFTEXT%tag2% #with $str = ~ s/\%(\S+)\%/$template{$1}/gi; #would yield: #$str eq $template{"tag1%ALONGSTRINGOFTEXT%tag2"};

      ...because the \S+ would match all occurances of muliple-non-whitespace-then-a-percent. In this case, for that test file, it works fine, because there is whitespace demarkating the replacement targets. (\S+?) is a really good idea, but I would also look into the problem in your %template

          --jb
      Try making your quantifier, in this case +, non-greedy, with a ? after it.

      I don't think this is necessary here. After all the tag is already surrounded by special symbols, the percentage sign ('%'). So why not just use the greedy modifier with a negated character class (of course you can't have any '%' chars in the tag names): s/\%([^%]+)\%/$template{$1}/g;

      -- Hofmator

Re: s// problem
by loc (Beadle) on Apr 12, 2002 at 00:36 UTC
    Hmm, I'm not sure what is happening here. I used the following and got the expected results:
    #!/usr/bin/perl -w use strict; my %template = ( tag1 => 'test1', tag2 => 'test2', ); my $tmp = "%tag1% went to %tag2%"; $tmp =~ s/\%(\S+)\%/$template{$1}/gi; print $tmp,"\n";
    and that produced:
    test1 went to test2
    
    What are the results you are getting?

    -loc

Re: s// problem
by Hofmator (Curate) on Apr 12, 2002 at 12:02 UTC

    What might be a problem, is that the /i modifier is not working the way you might expect. I think you don't want to mind about the capitalisation of your tags, ie. %TAG%, %tag% and %tAg% should all be equivalent.

    If this is the case, you have to store all your templates lowercase (or upper, doesn't make a difference) and then convert the captured string ($1) to this case. The /i modifier has only an influence on how the matching is done, the captured string is always exactly as it was in the original:

    'Hello World' =~ /(hello)/i; print $1; # prints 'Hello'

    So to solve your problem, you can take advantage of the /e modifier and write something like this:

    my %template = ( tag1 => 'test1', tag2 => 'test2', ); while (<DATA>) { print "before: $_"; s/%([^%]+)%/$template{lc($1)}/ge; print "after: $_"; } __DATA__ %tag1% some text %TAG2% some more text

    -- Hofmator