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

I'd like to replace part of a string with asterisks (obfuscating credit card numbers on a receipt.) Here's what I have already:
my $string = '5424123412345678'; substr($string,0,-4,'*' x (length($string) - 4)); print $string;
Can you think of a "better" way to do it? Meaning faster, cleaner, and/or shorter.

Update: No hard-coded asterisk strings, because not all credit card numbers are the same length. Also, consider this slight change to my original code:
my $string = '5424123412345678'; substr($string,4,-4,'*' x (length($string) - 8)); print $string;
That simple change will obfu all but the first four and last four digits. Play with that idea, if you'd like.

---
It's all fine and dandy until someone has to look at the code.

Replies are listed 'Best First'.
Re: Golf: Replacing part of a string with "*" (lvalue substr)
by tye (Sage) on Aug 18, 2006 at 15:44 UTC
    my $string= '62880147894644072'; substr( $string, 0, -4 ) =~ tr//*/c; print $string, $/;

    Note that I substituted my own credit card number for yours in my test code. ;)

    Update: ++ to Sidhekin for out golfing me while I was previewing. ( I like this solution because I find it clear, not because it involves fewer keystrokes. :)

    - tye        

      I like this solution a lot as well, both for clarity and because it maintains the flexibility of the original code demonstrated in the update to my original post. Very elegant.

      ---
      It's all fine and dandy until someone has to look at the code.
Re: Golf: Replacing part of a string with "*"
by jwkrahn (Abbot) on Aug 18, 2006 at 15:48 UTC
    $ perl -le' my $string = q/5424123412345678/; $string =~ s/.(?=.{4})/*/g; print $string;' ************5678 Update: $ perl -le' my $string = q/5424123412345678/; $string =~ s/(?<=.{4}).(?=.{4})/*/g; print $string;' 5424********5678
Re: Golf: Replacing part of a string with "*"
by Sidhekin (Priest) on Aug 18, 2006 at 15:43 UTC

    29, counting the semicolon. :-)

    my $string = '5424123412345678'; substr($string,0,-4)=~y//*/c; print $string;

    print "Just another Perl ${\(trickster and hacker)},"
    The Sidhekin proves Sidhe did it!

Re: Golf: Replacing part of a string with "*"
by Velaki (Chaplain) on Aug 18, 2006 at 15:54 UTC

    Here's one to be different:

    echo "1234567890123456" | perl -lne 'print"\*"x(length()-4),scalar reverse unpack "A4",reverse'

    gratias vobis ago valeteque
    -v.

    "Perl. There is no substitute."
Re: Golf: Replacing part of a string with "*"
by explorer (Chaplain) on Aug 18, 2006 at 15:25 UTC

    Maybe a regex:

    $string =~ s/^\d{12}/'*' x 12/e; # ************5678
      If the OP wanted to hardcode the length, he could have done
      substr($string,0,-4,'*'x12);

      Much clearer.

Re: Golf: Replacing part of a string with "*"
by ikegami (Patriarch) on Aug 18, 2006 at 15:28 UTC
    my $string = '5424123412345678'; substr$_,0,-4,'*'x(length()-4)for$string; # 41 print $string;
    my $string = '5424123412345678'; s/(?!.{0,4}$)./*/g for$string; # 30 print $string;

    Update: More readable than the previous:

    my $string = '5424123412345678'; $string=~s/(?!.{0,4}$)./*/g; # 28 print $string;
Re: Golf: Replacing part of a string with "*"
by explorer (Chaplain) on Aug 18, 2006 at 15:30 UTC

    More clean:

    $string = '*' x 12 . substr($string,-4); # ************5678
Re: Golf: Replacing part of a string with "*"
by BrowserUk (Patriarch) on Aug 18, 2006 at 15:23 UTC

    printf "%-12.12s****\n", '5424123412345678';;

    Update: Hmm. Misread your code. I thought you were obscuring the last 4 digits.

    This will do the first twelve for a score of ...um...either 0 or -30-somthing :)

    [0] Perl> printf "************%4.4s\n", '5424123412345678';; ************5424

    Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
    Lingua non convalesco, consenesco et abolesco. -- Rule 1 has a caveat! -- Who broke the cabal?
    "Science is about questioning the status quo. Questioning authority".
    In the absence of evidence, opinion is indistinguishable from prejudice.
      Still not right. It should be printing the *last* four (************5678).

        Ah! Then

        printf "************%s\n", substr('5424123412345678', -4);; ************5678

        Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
        Lingua non convalesco, consenesco et abolesco. -- Rule 1 has a caveat! -- Who broke the cabal?
        "Science is about questioning the status quo. Questioning authority".
        In the absence of evidence, opinion is indistinguishable from prejudice.
Re: Golf: Replacing part of a string with "*"
by explorer (Chaplain) on Aug 18, 2006 at 15:36 UTC

    More fuzzy (the result)

    substr($string,rand length $string,1,"*") foreach 1..12; # 5****23** +*34**7*
    Update: No guarantee included :-)
      That's bad for readability (harder for the user to figure out which one of his cards was used), and it's bad security (since the number could be obtained over time or if multiple retailers did this). And of course, there's are times when two, one and even no digits are hidden.

        I know one bank that use it... but I agree with you...

      Actually a couple issues with this, 1) not guaranteed to use any asterisks (usually will), 2) since they are different each time with enough receipts you could get the whole number.

                      - Ant
                      - Some of my best work - (1 2 3)