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

Excuse the newbie question. Given the following string:
my $string = 'fred=abcdef&wilbur=123456&fred=hijk';
The following code catches all cases except for the last.
sub obscure { my ($self, $s) = @_; use constant REPLACEMENT => '*'; my @tags = qw/fred wilbur/; for my $tag (@tags) { $s =~ s{$tag=(.*?)\&}{ "$tag=" . (REPLACEMENT x length($1)) . '&' }gex; $s =~ s/$tag=(.*)$/"$tag="/gex; # WRONG } return $s; }
How do I write the second substitution to catch the end-of-line case? Thanks.

Replies are listed 'Best First'.
Re: edge case in substitution?
by Roy Johnson (Monsignor) on May 17, 2005 at 23:06 UTC
    Where you have s{$tag=(.*?)\&}, just make it s{$tag=(.*?)(?:\&|$)}. So you match an ampersand or the end of string. Another way: s{$tag=([^&]*?)} should work

    Caution: Contents may have been coded under pressure.
Re: edge case in substitution?
by ysth (Canon) on May 17, 2005 at 23:53 UTC
    You have another edge case too: 'fred=abcdef&wilbur=123456&wilfred=hijk' What you want is to make sure the $tag=(.*?) part is preceded by & or the beginning of the string and followed by & or the end of the string. This looks like:
    s{(?:(?<=&)|^) $tag=(.*?) (?:(?=&)|\z) ...
    where (?<=&) and (?=&) are zero-width positive look-ahead and look-behind assertions. It can be simplified using negative assertions, at the cost of making it a little hard to wrap your head around initially, to:
    s{(?<![^&]) $tag=(.*?) (?![^&]) ...
    (Literally, match $tag=(.*?) only when not preceded or followed by a non-& character.)
Re: edge case in substitution?
by mrborisguy (Hermit) on May 17, 2005 at 23:08 UTC
    i actually don't know how to answer your exact question, because i've never used those advanced regex's (something to learn tomorrow!), but it looks like you're just trying to replace all of the sequences behind the names with asterisks, right?
    sub obscure { my ( $self, $s ) = @_; use constant REPLACEMENT => '*'; my @pairs = split /&/, $s; my @temp = map { my ( $name, $value ) = split /=/; $name . "=" . REPLACEMENT x length( $value ) } @pairs; return join '&', @temp; }
    or without temporary arrays
    sub obscure { my ( $self, $s ) = @_; use constant REPLACEMENT => '*'; return join '&', map { my ( $name, $value ) = split /=/; $name . "=" . REPLACEMENT x length( $value ) } split /&/, $s; }
      This is actually a nice approach, especially since it automatically deals with the other edge condition mentioned by ysth below, as well as the one cited in the OP.

      But I think you missed some of the details -- the monk only wants to put in asterisks for certain paramters in the string:

      sub obscure { my ($self, $s) = @_; my %tags = qw/fred x wilbur x/; return join '&', map { my ($n,$v) = split /=/; ( exists( %tags{$n} )) ? "$n=".'*' x length($v) : $_ } split /\&/, $s; }
      (Sorry, but I didn't see the relevance/need for "use constant" there, so I left it out.)

      (updated to fix indentation in the code)

Re: edge case in substitution?
by Anonymous Monk on May 18, 2005 at 03:39 UTC
    You use CGI.pm?