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

Here is a question I have come accross a couple of times but have never found a better way than
1 while(s/(pattern)/&do_something($1)/ge);
best example... say I have a simple template, and I replace a template item with text that has more template data in it, is there a way to make the regexp go back and continue from the spot it replaced at. This is more of a curiosity than anything else, there are certainly other ways to do it (like translating the text returned by do_something before it is returned...) but I would like to know. Obviously there is a danger of continuous looping if you could do this...

                - Ant

Replies are listed 'Best First'.
Re: replacing text you have already replaced...
by merlyn (Sage) on Jul 25, 2001 at 18:04 UTC
    You could save-and-restore pos:
    while (/(pattern)/g) { my $startpos = pos -= length $1; substr($_, $startpos, length $1, do_something($1)); pos = $startpos; }
    Basically open-coding the s///g operator with an extra last step to rescan what's already been seen.

    -- Randal L. Schwartz, Perl hacker


    update:

    Whoa. Unnecessarily complicated there. This is simpler:

    $_ = "aaa aaa aaa aaa"; while (/(aa)/g) { pos $_ -= length $1; substr($_, pos, length $1, "ba"); } print;
    This prints "bba bba bba bba", because the "a" being replaced is allowed to match part of the next "aa" search, as expected.
Re: replacing text you have already replaced...
by wine (Scribe) on Jul 24, 2001 at 22:13 UTC

    I always use 1 while s///;, but you can also get that behavior with only using s///;:

    my $data = "aaaaaaaaaaaaaaaaaaaaaaa"; # 23 a's $data =~ s/aa/pos() = print a/ge; print $data;

    This outputs a in stead of aaaaaaaaaaaa

    pos() normally returns the position where the regexpr matched the last time. Normally the s/// operator leaves of at that position. By using pos() as a lvalue you can reset this position. Therefore the s/// operator starts all over, mimmicking the 1 while s///g behavior.

    I wouldn't use this under normal circumstantions though, since this is kind of a obfu. ;)

      I wanted it to start from where it matched a token.. i.e.
      This is a template <TEMPLATE_DATA_FOOTER> mmm-kay
      and say <TEMPLATE_DATA_FOOTER> subs in <TEMPLATE_DATA_ADDRESS>... rather than restarting at the beginning I want to restart before <TEMPLATE_DATA_FOOTER>... I suppose I could set pos() = pos() - length($1) or something like that... that'd be weird...

                      - Ant

        I just had an idea for your problem, why not make your function recursive and do the repeated replacement there?

        hth
        -- Hofmator

      This does work as advertised but probably different than you thought. You should have used warnings ;-), your regex is equivalent to $data =~ s/aa//g; and that is not what you had in mind, is it? A small typo is the cause, you print a but I think wanted to print 'a', the letter 'a' and not nothing to the filehandle a.

      The important point is, pos() does not work with the substitution operator, only with m//g. See pos.

      So I guess we have to make do with the  1 while s/// solution.

      -- Hofmator

        I stand corrected, thank you for pointing it out. ;)