Jump to content

[SOLVED] Splitting a credit card number (last 4 digits)


techtheatre

Recommended Posts

I am trying to split up a credit card number that a user inputs through a form on my site (for the example let's say it is stored in a variable called "$CreditCardInput".  I think that all credit cards have 16 digits, but i am not sure...so to be safe i want to count from the END (right-side) of the string.  I want to split the last four digits into one variable and the first however many are left into another variable (lets say $CardStart and $LastFour).  I guess ideally spaces and other non-numeric data will be removed as well before the split happens.

 

Please not only show me how to do this, but if you are willing please explain what each part of the expression is actually doing so that i (and others) can learn WHY it works as well.  Eventually I will get regex figured out, but at the moment it is pretty overwhelming.

 

THANKS!

Link to comment
Share on other sites

I think there used to be some 15 and/or 14 digit cards, but I'm not sure it's still that way.  Google "credit card number authentication" and learn about it.  In fact, for slightly better authentication, look up the Luhn algorithm.

 

$CreditCardInput = preg_replace('/[^0-9]/','',$CreditCardInput);
preg_match('/(.*)(\d{4})$/',$CreditCardInput,$matches);
$CardStart = $matches[1];
$LastFour = $matches[2];

 

The first line removes everything that's not a digit.  The square brackets are a character class, meaning the whole bracketted expression matches one character (unless otherwise modified) if it matches the contents of the brackets UNLESS the first character is a caret (^), then reverse the character class.  0-9 is a range (digits).

 

The second line matches and captures the last four digits and the beginning of the string.  Parentheses capture subexpressions and put them in $matches.  \d{4} matches four digits anchored at the end of the string with $.  The .* matches everything else.

 

The whole matched pattern will be in $matches[0] and the subexpressions will be in 1,2,3...etc.

Link to comment
Share on other sites

I have plugged in the code you gave and it works perfectly...but there was one part of your explanation that i did not understand...can you clarify the necessity for the ^ symbol?  If we have already stripped out everything that is not a number, how would this symbol be in teh string?

 

...UNLESS the first character is a caret (^), then reverse the character class...

 

Thanks!

Link to comment
Share on other sites

The caret (^) -- at the beginning of a class -- negates the character class, so it matches everything BUT what's in the class, a logical NOT.  It's a character with special meaning inside a class, not the actual character itself (but only when it's the first character).

 

Examples:

[0-9]  // Match a digit.
[^0-9] // Match anything BUT a digit
[aeiuo]  // Match a vowel
[^aeiou]  // Match anything BUT a vowel
[%^&]  // Match the percent sign, caret, and ampersand (since the caret is not at the beginning of the class.

 

So, what the regular expression in the preg_replace does is match everything that's NOT a digit ([^0-9]) and replaces it with ... nothing ('').

Link to comment
Share on other sites

So i have one more question...this is the reverse of the question before...

what if i have the whole card number, but now i want to insert spaces every X characters?

 

Example:

$CreditCardInput = "123456789012345";
$SpaceEveryCharNum = "4";
//something magic happens here
$CreditCardOutput = "1234 5678 9012 345";

 

i assume it is something like:

preg_match('/(\d{$SpaceEveryCharNum })(\d{$SpaceEveryCharNum })$/',$CreditCardInput,$matches);
$CreditCardOutput = implode(" ",$matches);

 

except that the input number could (theoretically) be any length and it also really doesn't do me too much good having it in an array since i only need to insert spaces

 

Link to comment
Share on other sites

So i found a function for formatting a phone number (in the script examples section of this website) and have modified it below.  The two problems that remain are:

 

1.  i don't know for sure how many times the input string will have to be cut up (yes...this case it is just a credit card number, but i want the function to be re-usable in the future if the string is something else longer)

 

2.  I don't know if i can simply plug in my variable ($SpaceEveryCharNum) for the number of digits to skip each time wherever there is currently a "4" hard-coded, or if that will not work.

 

$CreditCardOutput = ereg_replace("([0-9]{4})([0-9]{4})([0-9]{4})([0-9]{4})([0-9]{4})", "\1 \2 \3 \4 \5", $CreditCardInput); 

Link to comment
Share on other sites

Another Update:  Unfortunately i cannot get the above function to work...i have tried all sorts of things, but it keeps giving me back the WHOLE string UNALTERED...i can't seem to figure out where the spaces go in...still working on it, but any assistance is greatly appreciated!  Thanks!

Link to comment
Share on other sites

so...now it seems that the function breaks if i search for too many "parts" to match...i have successfully formatted SOME of my string with the following:

 

$CreditCardInput = "987654321098765";

$CreditCardInput= ereg_replace("([0-9]{4})([0-9]{4})([0-9]{4})$", "\\1 \\2 \\3 --\\0", $CreditCardInput);

 

This is what i get:

9876543 2109 8765 --654321098765

 

I have NO idea what any of the above means...i have just been cut/paste lots of parts from anything i can find.  I have had a difficult time finding documentation of this function that makes ANY sense...WAY too complicated if you ask me.

 

If i add another ([0-9]{4}) to the search string and another  \\4 to the output string, it breaks and i end up back where i started with the full string.  Surely there is a way to add "repeating" search blocks?  Also (and again i do not know what i am talking about), it seems that it is searching from teh END of the string toward the start of the string...that is backwards from how this should work.

 

PLEASE HELP.  I am going to quit trying this anymore tonight and work on other parts that i actually know how to do...otherwise this will get to be WAY too frustrating.  Thanks in advance for anyone who can clear up any of this!

 

Link to comment
Share on other sites

This is getting pretty advanced, but this will insert spaces every 4 digits (from the end).

$text = '9876543210987654';
echo "$text\n";
$text = preg_replace('/(?<=\d)(?=(\d\d\d\d)+\z)/', ' ', $text);
echo "$text\n";

This uses lookarounds which actually don't "consume" any characters in the match, they essentially just match a location (point between two characters).

Link to comment
Share on other sites

cool...thanks...is there a way to (easily) make it start from the beginning of the string rather than the end...or is that just how preg works?...if that is difficult, then never mind...this will work fine for me.  THANKS!!!

Link to comment
Share on other sites

That's really just the way that this pattern works. (I adapted it from a pattern that puts commas in numbers, such as 1234567 => 1,234,567. That's why it goes from the end of the string, to account for the 1,... part.) I can't really see a way to get it to be anchored to the beginning (my two quick attempts just now both failed). If this is coming from form input or a database it shouldn't matter that its anchored to the end of the string, right?

Link to comment
Share on other sites

no...it really doesn't matter...i will just use this...my only thought was that if the character count is not a multiple of 4 then i could end up with somethign strange looking, like:

 

9 8765 4321 0987 6543

--or--

98 7654 3210 9876

 

this is not rally a problem thought...mostly just my curiosity to know if it could be accomplished forwards rather than backwards...i will mark it as solved, but if you think of a way to do it forwards, let me know.  thanks!

Link to comment
Share on other sites

right...but as this is a credit card number, i think they are always done in batches of four...except i think some card numbers vary in length...so that the last set of numbers might be less than four (typically 3).  Anyway, it doesn't matter as the code you wrote above works great...but for general purposes it might be more re-usable if it processed from the beginning...don't worry about it unless you just wand to figure it out for fun...but if you do, please let me know what you come up with.

 

THANKS FOR YOUR HELP!!!

 

Link to comment
Share on other sites

This thread is more than a year old. Please don't revive it unless you have something important to add.

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Restore formatting

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Create New...

Important Information

We have placed cookies on your device to help make this website better. You can adjust your cookie settings, otherwise we'll assume you're okay to continue.