11

I have read through this question, but for Discover card, the starting digits are 6011, 622126-622925, 644-649, 65 instead of just 6011, 65. (Source)

For Discover cards, I picked up this regex from that question ^6(?:011|5[0-9]{2})[0-9]{12}$

I modified it to cover 6011, 644-649& 65 but for 622126-622925, building regex is hard cuz of my poor regex skills.

I have this regex so far 6(?:011|5[0-9]{2}|[4][4-9][0-9]|[2]{2}[1-9])[0-9]{2}$, but it only checks for 622[1-9]**.

How do I modify it so that it accepts only between 622126-622925 for 622*** case?

Community
  • 1
  • 1
Ashfame
  • 1,731
  • 1
  • 25
  • 42
  • Could you just use the normal Discover Regex and do the check in a more readable way on the next line? – Yuriy Faktorovich Nov 21 '12 at 19:30
  • Any reason you won't just use non-regex numeric comparisons? – djechlin Nov 21 '12 at 19:31
  • @YuriyFaktorovich Sorry didn't get you. You mean formatting my question above or tackling it in 2 steps? If latter, I would prefer a single regex for that. – Ashfame Nov 21 '12 at 19:32
  • @djechlin I could do that, but all credit card checking is right now via regex and switching to integer comparisons because of poor regex skills seem unfair to my code. – Ashfame Nov 21 '12 at 19:33
  • 2
    I meant tackling it in 2 steps. If you create this complex, mostly unreadable Regex since you're not using comments in it, any changes will be very time wasting. You're also wasting time trying to use Regex to do a numeric comparison. Basically you're using a hammer to pound in a screw. – Yuriy Faktorovich Nov 21 '12 at 19:35
  • Also I don't buy the argument of switching to integer would be unfair to your code. You're doing the checksum with numbers anyway, aren't you? – Yuriy Faktorovich Nov 21 '12 at 19:37
  • Not all functionality has to be crammed into one single regex. Split it into multiple steps. – Andy Lester Nov 21 '12 at 19:54
  • You guys are right, breaking it down into 2 steps was easier & better to maintain. – Ashfame Nov 21 '12 at 19:56

3 Answers3

16

Here's your regex (demo):

^6(?:011\d{12}|5\d{14}|4[4-9]\d{13}|22(?:1(?:2[6-9]|[3-9]\d)|[2-8]\d{2}|9(?:[01]\d|2[0-5]))\d{10})$

Needless to say, I won't exactly call this pretty or easy to maintain. I would recommend parsing the number as an integer and using your programming language to do the checks.

You should also use Luhn algorithm to check if the credit card number is valid, and while you could theoretically do this with regex, it would many times worse than this.


Allow me to show you how I arrived at this monstrosity, step by step. First, here is how you match each of those ranges:

6011        # matches 6011
65          # matches 65
64[4-9]     # matches 644-649
622(1(2[6-9]|[3-9]\d)|[2-8]\d{2}|9([01]\d|2[0-5]))  
            # matches 622126-622925

Now, you want to match the rest of the digits:

6011\d{12}        # matches 6011 + 12 digits
65\d{14}          # matches 65 + 14 digits
64[4-9]\d{13}     # matches 644-649 + 13 digits
622(1(2[6-9]|[3-9]\d)|[2-8]\d{2}|9([01]\d|2[0-5]))\d{10}
                  # matches 622126-622925 + 10 digits

Now you can combine all four, and add start and end of line anchors:

^(                  # match start of string and open group
 6011\d{12}|        # matches 6011 + 12 digits
 65\d{14}|          # matches 65 + 14 digits
 64[4-9]\d{13}|     # matches 644-649 + 13 digits
 622(1(2[6-9]|[3-9]\d)|[2-8]\d{2}|9([01]\d|2[0-5]))\d{10}
                    # matches 622126-622925 + 10 digits
)$                  # close group and match end of string

The final product above is a slightly compacted version of the previous regex, and I also made groups non-capturing (that's what those ?: are for).

NullUserException
  • 83,810
  • 28
  • 209
  • 234
  • Holy shmoly! Now I see how it got complicated & beyond the point of my understanding. I just made it work by parsing it as an integer and comparing it for `622126-622925` case. Thanks Nerdlord for explaining this to me! :D – Ashfame Nov 21 '12 at 19:55
  • @Ashfame Yes, ranges in regex get unwieldy real fast. And like I said, you should also verify the check digit is correct; and that's something that you *really* shouldn't use a regex for. – NullUserException Nov 21 '12 at 19:58
  • Yes sir! I have a jQuery validation plugin for Luhn algorithm in place for that. – Ashfame Nov 21 '12 at 20:01
  • This test card is not working for me, while on others systems it works, any ideas? 6011 1111 1111 1117 – lucasvm1980 Sep 14 '17 at 21:22
  • 2
    I modified this regex to increase the 16 digit limit to 19 digits: /^6(?:011\d{12,15}|5\d{14,17}|4[4-9]\d{13,16}|22(?:1(?:2[6-9]|[3-9]\d)|[2-8]\d{2}|9(?:[01]\d|2[0-5]))\d{10,13})$/ This wikipedia page lists the range as 16 - 19 digits: https://en.wikipedia.org/wiki/Payment_card_number This page has Discover test cards with 16 and 19 digits: https://www.freeformatter.com/credit-card-number-generator-validator.html – Josh Dec 13 '18 at 16:46
  • "verify the check digit is correct; and that's something that you really shouldn't use a regex for" - I'd like to see you try. Perhaps one for https://codegolf.stackexchange.com/ – mjaggard Jul 16 '21 at 13:47
0

Here are your options:

  1. Hack your way through it and build a really complicated regex. Regexes are not suited for this sort of integer comparison so what you come up with will necessarily be long, uncomplicated and unmaintainable. See Regex for number check below a value and similar SO questions on this topic.
  2. Use integer comparison in your code.

For reference one such said complicated regex would be

62212[6-9]|6221[3-9]|622[1-8]|62291|62292[1-5]

Community
  • 1
  • 1
djechlin
  • 59,258
  • 35
  • 162
  • 290
0

even this ticket is 3 years ago, I encountered the same task and would like to share a regex for 622126-622925 :)

^(622[1-9]\\d(?<!10|11|9[3-9])\\d(?<!12[0-5]|92[6-9])\\d{10})$

which using zero-width negative lookbehind to exclude not expected number

weiweitop
  • 1
  • 3