Forum Moderators: coopster

Message Too Old, No Replies

preg match and regex

         

Crump

3:49 pm on Jan 13, 2012 (gmt 0)

10+ Year Member



I am struggling with a regex and it's so simple...

if (!preg_match("/[0-9-]/", $creditcardnumber) {
echo "this isn't a valid credit card number";
}

I realize there is more to a valid credit card number, but I have several other tests. Basically I want to output an error if the string doesn't contain only numbers, dashes, and spaces. What am I doing wrong?

eelixduppy

4:49 pm on Jan 13, 2012 (gmt 0)



That last dash in there is probably causing issues. Try a pattern like this:


$pattern = "/[0-9\\- ]+/";


See also [regular-expressions.info...] for more advanced regex for credit card numbers.

rocknbil

4:54 pm on Jan 13, 2012 (gmt 0)

WebmasterWorld Senior Member 10+ Year Member



(simul-post, below says the same thing sorta. :-)

This is really an error prone and insufficient approach - if you search for "Mod 10 credit card" you'll find some code that does all this, and identifies a valid CC algo for any cards - that is, matches visa to the number, MC to the number, etc.

$is_valid = my_mod_10_function($ccnum);

If you want to forge forward . . .

In a character class - means "this to this" as in a-z = all letters, 0-9 all digits, so 0-9- may mean "zero to nine to . . . a space?" You could escape it.

Second, \d and 0-9 are equivalent, and a literal space is OK I guess but \s means "all whitespace".

Also, you're only looking for one character ( [], not []+ ) You may want to at least set a lowest end length in the match. Try

if (!preg_match("/^[\d\-\s]{12,}$/", $creditcardnumber) {
echo "this isn't a valid credit card number";
}

^ starts with
[] any of these
\d any digit
\- the literal -
\s any white space
{12,} a minimum of 12 to infinity characters
$ ends with (in a regex, this is not a variable but defines that the string must end here)

You can do the math and figure out the longest it will be, but this is still error prone (double spaces or -- ). It would look something like

"/^[\d\-\s]{12,30}$/"

Crump

5:11 pm on Jan 13, 2012 (gmt 0)

10+ Year Member



Thanks for the replies. I realize this is very basic... I do other credit card checking already including mod10. This is more like an exercise in regex for me at this point.

Your line:
if (!preg_match("/^[\d\-\s]{12,}$/", $creditcardnumber)) {

(with the parenthesis fixed)... how come if I change it to this:

if (!preg_match("/^[\d\-\s]$/", $creditcardnumber)) {

I removed the {12,} and now a set of 16 digits doesn't validate and returns an error?

eelixduppy

5:24 pm on Jan 13, 2012 (gmt 0)



The dollar character ($) signifies the end of the string you are matching against, whereas the caret character (^) signifies the beginning of the string. Your new pattern will only match successfully against a string that contains a single character. This is because you removed the {12,} which matches a repeated pattern before the end of the string.

Crump

5:58 pm on Jan 13, 2012 (gmt 0)

10+ Year Member



After looking around a bunch of trial and error, this is what I came up with:

if (preg_match("/[^0-9 -]+/", $creditcardnumber)) {

How does this look? I had to take out the ! at the front, and I don't even really know why. This checks for digits, dashes, and spaces.

I have been programming PHP for years, and I hate regex. I've tried reading books on it too. It's so cryptic.

Here is a quick question. Are these two lines the same thing?

if (preg_match("/[^0-9 -]+/", $creditcardnumber)) {

if (!preg_match("/[0-9 -]+/", $creditcardnumber)) {

eelixduppy

6:32 pm on Jan 13, 2012 (gmt 0)



>> Are these two lines the same thing?

Definitely not.

As I'm sure you know, the use of [] is to declare a character class. So for example [0-9] will match a single number char. The syntax to negate a character class is by adding the caret to the beginning of the declaration: [^0-9] matches anything EXCEPT for a single number char.


if (preg_match("/[^0-9 -]+/", $creditcardnumber)) {


in English this means, if creditcardnumber contains at least one character that is not 0-9, <space>, or - then...

this matches 'test' and 'test123'


if (!preg_match("/[0-9 -]+/", $creditcardnumber)) {


in English this means, if creditcardnumber does not contain at least one character that is 0-9, <space>, or - then...

this matches 'test' but not 'test123'


With that said, you are looking for something more along the lines of this...

if(preg_match('/^[0-9 \-]+$/', $creditcardnumber)) {


...which takes into account the start and end of the credit card string.

>> It's so cryptic.

With regex you learn by doing than by reading. It takes years to become an expert, but to be honest I think it is one of the most useful tools one could learn. I primarily use it to grep log files on my servers.

g1smd

8:16 pm on Jan 13, 2012 (gmt 0)

WebmasterWorld Senior Member 10+ Year Member Top Contributors Of The Month



Before you start coding write out in English EXACTLY what you want the code to do.

These will each be coded differently:
- contains digits
- does not contain digits
- is exactly x characters long
- is exactly x digits long
- etc.

At the very least, you should remove all spaces from the input, then test for exactly 16 digits.

rocknbil

9:09 pm on Jan 16, 2012 (gmt 0)

WebmasterWorld Senior Member 10+ Year Member



I removed the {12,} and now a set of 16 digits doesn't validate and returns an error?


Because you removed the quantifier. Your result was then, roughly, "begins and ends with ONE character, a number, dash, or space."

^[\d\-\s]$ Begins, contains only, and ends with ONE of these characters
^[\d\-\s]*$ Begins, contains only, and ends with zero or more of these characters (worthless in this case)
^[\d\-\s]+$ Begins, contains only, and ends with one or more of these characters
^[\d\-\s]{12,16}$ Begins, contains only, and ends with 12 to 16 of these characters (also really not very helpful, it could match on 16 dashes or 16 spaces)

I had to take out the ! at the front, and I don't even really know why.


! is not, so "if it doesn't match, error" turns into "if it matches, OK, do this."

if (!preg_match(expression)) {
echo "doesn't match";
}

if (preg_match(expression)) {
echo "it matches on something, though I'm not sure what. :-)";
}

The only useful exercise for credit card numbers -because they vary in length based on what type of card it is - would have to include a card type and if statements. See E's link on advanced CC regexes for a taste.