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

Hi,

I need to convert a credit card number in the format of 4744760012394122 to display with spaces every four characters (ie: 4744 7600 1239 4122).

How's this done? My best guess is 'sprintf'.

Thanks,
Ralph.
  • Comment on Simple formatting question for credit card number

Replies are listed 'Best First'.
Re: Simple formatting question for credit card number
by antirice (Priest) on Aug 12, 2003 at 20:45 UTC

    Not to be a stick in the mud, but please remember that not all credit cards are 16 digits long. For instance, AMEX is 15 digits (4-6-5 or s/(.{4})(.{6})(.{5})/$1 $2 $3/) and some VISA cards are 13 digits (4-3-3-3 or s/(.{4})(.{3})(.{3})(.{3})/$1 $2 $3 $4/).

    On a side note, you can use Business::CreditCard to tell what type of credit card you are dealing with as well as whether or not the credit card's checksum is valid.

    Hope this helps.

    antirice    
    The first rule of Perl club is - use Perl
    The
    ith rule of Perl club is - follow rule i - 1 for i > 1

      If you end up with a simple function that normalizes the punctuation of various credit cards based on type, I'd suggest submitting it as a contribution to Business::CreditCard. It'd be a nice contribution and an obvious place for people to look for it in the future.
Re: Simple formatting question for credit card number
by dragonchild (Archbishop) on Aug 12, 2003 at 20:23 UTC
    As not all credit card numbers have exactly 16 digits, the best thing to do is to build the spacing in reverse.
    sub format_cc_num { my ($num, $spacer) = @_; $num = reverse $num; $num =~ s/(.{4})/${1}${spacer}/g; return reverse $num; }

    ------
    We are the carpenters and bricklayers of the Information Age.

    The idea is a little like C++ templates, except not quite so brain-meltingly complicated. -- TheDamian, Exegesis 6

    Please remember that I'm crufty and crochety. All opinions are purely mine and all code is untested, unless otherwise specified.

      Careful. Context will trip you up when playing with reverse. ++ otherwise.
      return scalar reverse $num;
      Why does everyone reach for substitution though?
      sub format_cc_num { my ($num, $spacer) = @_; scalar reverse join $spacer, (reverse $num) =~ /(....)/g; }

      Makeshifts last the longest.

        Actually, we both were buggy. Using your code and my code (with the appropriate bugfix), we have:
        #!/usr/bin/perl use strict; use warnings; my $x = "12345678"; my $y = "123456789"; sub dragonchild { my ($n, $s) = @_; $n = reverse $n; # Added negative lookahead $n =~ s/(.{4})(?!$)/${1}${s}/g; scalar reverse $n; } sub aristotle { my ($n, $s) = @_; scalar reverse join $s, (reverse $n) =~ /(....)/g; } foreach my $v ($x, $y) { print dragonchild($v, '-'), $/; print aristotle($v, '-'), $/; } ------ 1234-5678 1234-5678 1-2345-6789 2345-6789

        Your code only works for card numbers that are a multiple of 4. Most department store cards are 11 digits and some, like AmEx, are moving to 17 or 19 digits. (I used to work for MC ... we had a similar function that I had to maintain in the app I worked on. It was written horribly with substr and the like.)

        ------
        We are the carpenters and bricklayers of the Information Age.

        The idea is a little like C++ templates, except not quite so brain-meltingly complicated. -- TheDamian, Exegesis 6

        Please remember that I'm crufty and crochety. All opinions are purely mine and all code is untested, unless otherwise specified.

Re: Simple formatting question for credit card number
by Limbic~Region (Chancellor) on Aug 12, 2003 at 20:11 UTC
    Anonymous Monk,
    Because I am lacking in creativity, I will give you the first thing (and probably not the best thing) that came to mind).
    #!/usr/bin/perl -w use strict; my $number = '4744760012394122'; if ($number =~ /^(\d{4})(\d{4})(\d{4})(\d{4})$/) { $number = "$1 $2 $3 $4"; } else { print "Unable to convert $number\n"; }
    I hope that wasn't your real credit card number.

    Cheers - L~R

      I would use:
      $number =~ s#(.{4})#$1 #g;

      There should be a quick way doing this with unpack(), but the heat just got to me... ;-)

      Liz

      Update
      Hmmm... that puts a space at the end. which may or may not be a problem. And it doesn't check for digits. So I guess this would be better:

      $number =~ s#(\d{4})(?=\d)#$1 #g;
      or:
      $number = "@{[unpack '(A4)*', $number]}";
      but that is heading towards obfuscation ;-) (thanks tedrek!)
        print join " ", unpack("A4A4A4A4", $number)
Re: Simple formatting question for credit card number
by BrowserUk (Patriarch) on Aug 12, 2003 at 20:44 UTC

    TIMTOWTDI.

    print join ' ', '4744760012394122' =~ m[(....)]g 4744 7600 1239 4122

    Examine what is said, not who speaks.
    "Efficiency is intelligent laziness." -David Dunham
    "When I'm working on a problem, I never think about beauty. I think only how to solve the problem. But when I have finished, if the solution is not beautiful, I know it is wrong." -Richard Buckminster Fuller
    If I understand your problem, I can solve it! Of course, the same can be said for you.

Re: Simple formatting question for credit card number
by blue_cowdawg (Monsignor) on Aug 12, 2003 at 20:17 UTC

    Check it out:

        #!/usr/bin/perl -w ################################### use strict; use warnings; use diagnostics; my $cc="4744760012394122"; $cc =~ s/(\d{4})(\d{4})(\d{4})(\d{4})/$1 $2 $3 $4/; print $cc,"\n";
    perldoc perlre is your friend....


    Peter @ Berghold . Net

    Sieze the cow! Bite the day!

    Nobody expects the Perl inquisition!

    Test the code? We don't need to test no stinkin' code!
    All code posted here is as is where is unless otherwise stated.

    Brewer of Belgian style Ales

Re: Simple formatting question for credit card number
by hardburn (Abbot) on Aug 12, 2003 at 20:35 UTC

    My attempt.

    my $cc_num; # Intitilized elsewhere print join ' ', unpack('A4 A4 A4 A4', $cc_num);

    ----
    I wanted to explore how Perl's closures can be manipulated, and ended up creating an object system by accident.
    -- Schemer

    Note: All code is untested, unless otherwise stated

Re: Simple formatting question for credit card number
by tedrek (Pilgrim) on Aug 13, 2003 at 01:59 UTC

    And let's throw a substr solution into the mix as well :)

    $n = "4744760012394122"; $r = substr($n,-4,4,"")." ".$r while $n ne "";