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

I need to convert tilde characters (\x7E) to a character pair (\x7D\x5E), but only if it's not the very last character. This bit fails:
$output =~ s/\x7E/\x7D\x5E/g;
because it also escapes the last character in the string. Can this be done with a regex, or should I just give in and write a loop?

Thank you.
Regards,
Rich

Replies are listed 'Best First'.
Re: Regular expression substitution question
by duff (Parson) on Mar 31, 2004 at 18:19 UTC
    Sure it can be done ...
    $output =~ s/\x7E(?!$)/\x7D\x5E/g;

    See perlre where it talks about zero-width lookaheads

      And for us anal-retentive types who hate to replace something with itself:
      s/(?<=\x7E(?!$))/\x5E/g;
      Oh, just shoot me now. I didn't notice 7E->7D. I must not be anal-retentive enough. Thanks, Enlil.
      s/~(?!$)/}^/g;

      The PerlMonk tr/// Advocate
      Thanks for the answers. This indeed works quite well.
Re: Regular expression substitution question
by Enlil (Parson) on Mar 31, 2004 at 18:20 UTC
    Try:
    $output =~ s/\x7E(?!$)/\x7D\x5E/g;
    update:Yeah, what he said (alas, beaten to the punch again)

    -enlil

Re: Regular expression substitution question
by davido (Cardinal) on Mar 31, 2004 at 18:20 UTC
    The idea I've employed here is to use a positive zero-width lookahead assertion to ensure that a match can only occur if there is one more character following the tilde.

    $output =~ s/\x7E(?=.)/\x7D\x5E/g;

    It could also be expressed without the zero-width assertion like this:
    $output =~ s/\x7E\G(.)/\x7D\x5E$1/g;
    But the latter will probably run slower because it requires capturing parens.

    Update: Use the solutions with lookahead (Enlil's, duff's, or mine); they work, whereas the method that I've "crossed out" skips occurrences of the tilde if they're contiguous.


    Dave