techtheatre Posted May 1, 2007 Share Posted May 1, 2007 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! Quote Link to comment Share on other sites More sharing options...
Wildbug Posted May 1, 2007 Share Posted May 1, 2007 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. Quote Link to comment Share on other sites More sharing options...
techtheatre Posted May 2, 2007 Author Share Posted May 2, 2007 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! Quote Link to comment Share on other sites More sharing options...
Wildbug Posted May 2, 2007 Share Posted May 2, 2007 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 (''). Quote Link to comment Share on other sites More sharing options...
techtheatre Posted May 2, 2007 Author Share Posted May 2, 2007 Excellent! That makes MUCH more sense now. Thank you very much for your help! Quote Link to comment Share on other sites More sharing options...
techtheatre Posted May 5, 2007 Author Share Posted May 5, 2007 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 Quote Link to comment Share on other sites More sharing options...
techtheatre Posted May 5, 2007 Author Share Posted May 5, 2007 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); Quote Link to comment Share on other sites More sharing options...
techtheatre Posted May 6, 2007 Author Share Posted May 6, 2007 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! Quote Link to comment Share on other sites More sharing options...
techtheatre Posted May 6, 2007 Author Share Posted May 6, 2007 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! Quote Link to comment Share on other sites More sharing options...
c4onastick Posted May 6, 2007 Share Posted May 6, 2007 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). Quote Link to comment Share on other sites More sharing options...
techtheatre Posted May 6, 2007 Author Share Posted May 6, 2007 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!!! Quote Link to comment Share on other sites More sharing options...
c4onastick Posted May 7, 2007 Share Posted May 7, 2007 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? Quote Link to comment Share on other sites More sharing options...
techtheatre Posted May 7, 2007 Author Share Posted May 7, 2007 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! Quote Link to comment Share on other sites More sharing options...
c4onastick Posted May 7, 2007 Share Posted May 7, 2007 Very true, but if you anchor it to the start of the string the converse would be true and you'd get things like: 9876 5432 1987 6543 21 It's an interesting problem, I'll let you know if I figure something, this could be fun. Quote Link to comment Share on other sites More sharing options...
techtheatre Posted May 7, 2007 Author Share Posted May 7, 2007 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!!! Quote Link to comment Share on other sites More sharing options...
Recommended Posts
Join the conversation
You can post now and register later. If you have an account, sign in now to post with your account.