正则表达式信用卡号测试

发布于 2025-01-06 02:39:54 字数 418 浏览 4 评论 0原文

我正在测试一个应用程序,其中正则表达式模式与信用卡匹配,然后应该突出显示这些数字。我正在使用网站 http://regexpal.com/ 来创建测试信用卡号以进行测试。 测试它时,我没有成功建立这样的号码

我的要求是拥有有效的信用卡号码,其中可以有“-”和/或“,”。当我使用网站http://regexpal.com

我需要一些信用号码,其场景低于

  1. 有效的信用卡号码,任何数字之间可以有“-”。
  2. 有效的信用卡号,任意数字之间可以有“,”。
  3. 有效的信用卡号,任意数字之间可以包含“,”或“-”的组合。

I'm testing one application where Regex pattern match credit card then such numbers should be highlighted. I'm using site http://regexpal.com/ to create test credit credit card numbers for my testing. my requirement is to have valid credit card numbers which can have "-" and/or "," between them.I was not successful to build such a number as when i test it using the site

http://regexpal.com.

I need few credit numbers with scenarios below

  1. valid credit card number which can have "-" between any digit.
  2. valid credit card number which can have "," between any digit.
  3. valid credit card number which can have cobination of "," or "-" between any digit.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(16

染年凉城似染瑾 2025-01-13 02:39:54

常见信用卡供应商正则表达式:

  • Amex 卡:^3[47][0-9]{13}$
  • BCGlobal:^(6541|6556)[0-9]{12} $
  • Carte Blanche 卡:^389[0-9]{11}$
  • Diners Club 卡: ^3(?:0[0-5]|[68][0-9])[0-9]{11}$
  • 发现卡: ^65[4-9][0-9]{13}|64[4-9][0-9]{13}|6011[0-9]{12}|(622(?:12 [6- 9]|1[3-9][0-9]|[2-8][0-9][0-9]|9[01][0-9]|92[0-5])[0 -9]{10})$
  • Insta 支付卡:^63[7-9][0-9]{13}$
  • JCB 卡:^(?:2131|1800|35\d{3})\d {11}$
  • KoreanLocalCard:^9[0-9]{15}$
  • 激光卡: ^(6304|6706|6709|6771)[0-9]{12,15}$
  • 大师卡:^(5018|5020|5038|6304|6759|6761|6763) [0-9]{8,15}$
  • 万事达卡: <代码>^(5[1-5][0-9]{14}|2(22[1-9][0-9]{12}|2[3-9][0-9]{13 }|[3-6][0-9]{14}|7[0-1][0-9]{13}|720[0-9]{12}))$
  • 单卡: ^(6334|6767)[0-9]{12}|(6334|6767)[0-9]{14}|(6334|6767)[0-9]{15}$
  • 切换卡: <代码>^(4903|4905|4911|4936|6333|6759)[0-9]{12}|(4903|4905|4911|4936|6333|6759)[0-9]{14}|(4903 |4905|4911|4936|6333|6 759)[0-9]{15}|564182[0-9]{10}|564182[0-9]{12}|564182[0-9]{13}|633110[0-9]{10} |633110[0-9]{12}|633110[0-9]{13}$
  • 银联卡:^(62[0-9]{14,17})$
  • Visa卡:^4[0-9]{12}(?:[0-9] {3})?$
  • Visa Master 卡:^(?:4[0-9]{12}(?:[0-9]{3})?|5[1-5] [0-9]{14})$

Common credit card vendor regular expressions:

  • Amex Card: ^3[47][0-9]{13}$
  • BCGlobal: ^(6541|6556)[0-9]{12}$
  • Carte Blanche Card: ^389[0-9]{11}$
  • Diners Club Card: ^3(?:0[0-5]|[68][0-9])[0-9]{11}$
  • Discover Card: ^65[4-9][0-9]{13}|64[4-9][0-9]{13}|6011[0-9]{12}|(622(?:12[6-9]|1[3-9][0-9]|[2-8][0-9][0-9]|9[01][0-9]|92[0-5])[0-9]{10})$
  • Insta Payment Card: ^63[7-9][0-9]{13}$
  • JCB Card: ^(?:2131|1800|35\d{3})\d{11}$
  • KoreanLocalCard: ^9[0-9]{15}$
  • Laser Card: ^(6304|6706|6709|6771)[0-9]{12,15}$
  • Maestro Card: ^(5018|5020|5038|6304|6759|6761|6763)[0-9]{8,15}$
  • Mastercard: ^(5[1-5][0-9]{14}|2(22[1-9][0-9]{12}|2[3-9][0-9]{13}|[3-6][0-9]{14}|7[0-1][0-9]{13}|720[0-9]{12}))$
  • Solo Card: ^(6334|6767)[0-9]{12}|(6334|6767)[0-9]{14}|(6334|6767)[0-9]{15}$
  • Switch Card: ^(4903|4905|4911|4936|6333|6759)[0-9]{12}|(4903|4905|4911|4936|6333|6759)[0-9]{14}|(4903|4905|4911|4936|6333|6759)[0-9]{15}|564182[0-9]{10}|564182[0-9]{12}|564182[0-9]{13}|633110[0-9]{10}|633110[0-9]{12}|633110[0-9]{13}$
  • Union Pay Card: ^(62[0-9]{14,17})$
  • Visa Card: ^4[0-9]{12}(?:[0-9]{3})?$
  • Visa Master Card: ^(?:4[0-9]{12}(?:[0-9]{3})?|5[1-5][0-9]{14})$
世界如花海般美丽 2025-01-13 02:39:54

首先从字符串中删除所有 - 以及其他非数字。

然后使用此正则表达式来匹配 Visa、MasterCard、American Express、Diners Club、Discover 和 JCB 卡:

^(?:4[0-9]{12}(?:[0-9]{3})?|[25][1-7][0-9]{14}|6(? :011|5[0-9][0-9])[0-9]{12} |3[47][0-9]{13}|3(?:0[0-5]|[68][0-9])[0-9]{11}|(?:2131|1800| 35\d{3})\d{11})$

Remove all , and - and other non-digits from the string first.

Then use this regex that matches Visa, MasterCard, American Express, Diners Club, Discover, and JCB cards:

^(?:4[0-9]{12}(?:[0-9]{3})?|[25][1-7][0-9]{14}|6(?:011|5[0-9][0-9])[0-9]{12}|3[47][0-9]{13}|3(?:0[0-5]|[68][0-9])[0-9]{11}|(?:2131|1800|35\d{3})\d{11})$

熊抱啵儿 2025-01-13 02:39:54

2019 年

。不是。使用。 REGEX !!! (带有 3 个感叹号)


从评论中,我必须强调 PeteWiFi 的评论:

只是一个友好的警告,如果你尝试这样做,你将陷入一个受伤的世界
以这种方式匹配特定的方案和卡长度。例如,开关
自2002年以来就不存在了,Laser于2014年被撤回,Visa到期
发行 19 位数字卡,万事达卡现于 2xxxxx 发行
范围,只是为了强调这种方法的几个问题。一个
正则表达式适用于基本的“它看起来像卡号吗”,但不是
远不止于此。

如果您想使用正则表达式只是为了了解卡品牌用于视觉用途(例如显示 Visa 徽标或标签),那没问题。但如果你的代码逻辑依赖于它,那么不要使用正则表达式,也不要使用第 3 方插件/库!

正则表达式检测卡号快速且简单简单的。但从长远来看,你的项目将会遇到许多严重的问题。难以解决的错误。发卡机构不断推出新的卡号模式,或者撤回旧的卡号模式,或者可能彻底倒闭。谁知道呢。


解决方案

根据一些经常更新的官方页面构建您自己的解决方案(最好是非正则表达式),例如 此页面维基百科

至于“-”、“.”、“空格”和所有其他噪音,只需删除所有这些非数字,就可以使用这个(基于此答案):

$number = preg_replace("/[^0-9]/", "", "4111-1111 1111.1111");
// Output: 4111111111111111

还不相信?

此页面深入探讨了正则表达式为何如此的深入技术细节地狱。 (请注意,这篇文章使用了“地狱”这个词,因为一旦你进去了,你就无法出去)

编辑

这是我开发的解决方案(在 PHP 中):

// Based on https://en.wikipedia.org/wiki/Payment_card_number
// This constant is used in get_card_brand()
// Note: We're not using regex anymore, with this approach way we can easily read/write/change bin series in this array for future changes
// Key     (string)           brand, keep it unique in the array
// Value   (array)            for each element in the array:
//   Key   (string)           prefix of card number, minimum 1 digit maximum 6 digits per prefix. You can use "dash" for range. Example: "34" card number starts with 34. Range Example: "34-36" (which means first 6 digits starts with 340000-369999) card number starts with 34, 35 or 36
//   Value (array of strings) valid length of card number. You can set multiple ones. You can also use "dash" for range. Example: "16" means length must be 16 digits. Range Example: "15-17" length must be 15, 16 or 17. Multiple values example: ["12", "15-17"] card number can be 12 or 15 or 16 or 17 digits
define('CARD_NUMBERS', [
    'american_express' => [
        '34' => ['15'],
        '37' => ['15'],
    ],
    'diners_club' => [
        '36'      => ['14-19'],
        '300-305' => ['16-19'],
        '3095'    => ['16-19'],
        '38-39'   => ['16-19'],
    ],
    'jcb' => [
        '3528-3589' => ['16-19'],
    ],
    'discover' => [
        '6011'          => ['16-19'],
        '622126-622925' => ['16-19'],
        '624000-626999' => ['16-19'],
        '628200-628899' => ['16-19'],
        '64'            => ['16-19'],
        '65'            => ['16-19'],
    ],
    'dankort' => [
        '5019' => ['16'],
        //'4571' => ['16'],// Co-branded with Visa, so it should appear as Visa
    ],
    'maestro' => [
        '6759'   => ['12-19'],
        '676770' => ['12-19'],
        '676774' => ['12-19'],
        '50'     => ['12-19'],
        '56-69'  => ['12-19'],
    ],
    'mastercard' => [
        '2221-2720' => ['16'],
        '51-55'     => ['16'],
    ],
    'unionpay' => [
        '81' => ['16'],// Treated as Discover cards on Discover network
    ],
    'visa' => [
        '4' => ['13-19'],// Including related/partner brands: Dankort, Electron, etc. Note: majority of Visa cards are 16 digits, few old Visa cards may have 13 digits, and Visa is introducing 19 digits cards
    ],
]);

/**
 * Pass card number and it will return brand if found
 * Examples:
 *     get_card_brand('4111111111111111');                    // Output: "visa"
 *     get_card_brand('4111.1111 1111-1111');                 // Output: "visa" function will remove following noises: dot, space and dash
 *     get_card_brand('411111######1111');                    // Output: "visa" function can handle hashed card numbers
 *     get_card_brand('41');                                  // Output: "" because invalid length
 *     get_card_brand('41', false);                           // Output: "visa" because we told function to not validate length
 *     get_card_brand('987', false);                          // Output: "" no match found
 *     get_card_brand('4111 1111 1111 1111 1111 1111');       // Output: "" no match found
 *     get_card_brand('4111 1111 1111 1111 1111 1111', false);// Output: "visa" because we told function to not validate length
 * Implementation Note: This function doesn't use regex, instead it compares digit by digit. 
 *                      Because we're not using regex in this function, it's easier to add/edit/delete new bin series to global constant CARD_NUMBERS
 * Performance Note: This function is extremely fast, less than 0.0001 seconds
 * @param  String|Int $cardNumber     (required) Card number to know its brand. Examples: 4111111111111111 or 4111 1111-1111.1111 or 411111###XXX1111
 * @param  Boolean    $validateLength (optional) If true then will check length of the card which must be correct. If false then will not check length of the card. For example you can pass 41 with $validateLength = false still this function will return "visa" correctly
 * @return String                                returns card brand if valid, otherwise returns empty string
 */
function get_card_brand($cardNumber, $validateLength = true) {
    $foundCardBrand = '';
    
    $cardNumber = (string)$cardNumber;
    $cardNumber = str_replace(['-', ' ', '.'], '', $cardNumber);// Trim and remove noise
    
    if(in_array(substr($cardNumber, 0, 1), ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9'])) {// Try to find card number only if first digit is a number, if not then there is no need to check
        $cardNumber = preg_replace('/[^0-9]/', '0', $cardNumber);// Set all non-digits to zero, like "X" and "#" that maybe used to hide some digits
        $cardNumber = str_pad($cardNumber, 6, '0', STR_PAD_RIGHT);// If $cardNumber passed is less than 6 digits, will append 0s on right to make it 6
        
        $firstSixDigits   = (int)substr($cardNumber, 0, 6);// Get first 6 digits
        $cardNumberLength = strlen($cardNumber);// Total digits of the card
        
        foreach(CARD_NUMBERS as $brand => $rows) {
            foreach($rows as $prefix => $lengths) {
                $prefix    = (string)$prefix;
                $prefixMin = 0;
                $prefixMax = 0;
                if(strpos($prefix, '-') !== false) {// If "dash" exist in prefix, then this is a range of prefixes
                    $prefixArray = explode('-', $prefix);
                    $prefixMin = (int)str_pad($prefixArray[0], 6, '0', STR_PAD_RIGHT);
                    $prefixMax = (int)str_pad($prefixArray[1], 6, '9', STR_PAD_RIGHT);
                } else {// This is fixed prefix
                    $prefixMin = (int)str_pad($prefix, 6, '0', STR_PAD_RIGHT);
                    $prefixMax = (int)str_pad($prefix, 6, '9', STR_PAD_RIGHT);
                }

                $isValidPrefix = $firstSixDigits >= $prefixMin && $firstSixDigits <= $prefixMax;// Is string starts with the prefix

                if($isValidPrefix && !$validateLength) {
                    $foundCardBrand = $brand;
                    break 2;// Break from both loops
                }
                if($isValidPrefix && $validateLength) {
                    foreach($lengths as $length) {
                        $isValidLength = false;
                        if(strpos($length, '-') !== false) {// If "dash" exist in length, then this is a range of lengths
                            $lengthArray = explode('-', $length);
                            $minLength = (int)$lengthArray[0];
                            $maxLength = (int)$lengthArray[1];
                            $isValidLength = $cardNumberLength >= $minLength && $cardNumberLength <= $maxLength;
                        } else {// This is fixed length
                            $isValidLength = $cardNumberLength == (int)$length;
                        }
                        if($isValidLength) {
                            $foundCardBrand = $brand;
                            break 3;// Break from all 3 loops
                        }
                    }
                }
            }
        }
    }
    
    return $foundCardBrand;
}

2019

DO. NOT. USE. REGEX !!! (with 3 exclamation marks)


From the comments, I must highlight PeteWiFi's comment:

Just a friendly warning, you're in for a world of hurt if you try and
match specific schemes and card lengths this way. For example, Switch
hasn't existed since 2002, Laser was withdrawn in 2014, Visa are due
to issue 19 digit cards and MasterCard are now issuing in the 2xxxxx
ranges, just to highlight a couple of issues with this approach. A
regex is good for a basic "does it look like a card number" but not
much beyond that.

If you want to use regex just to know the card brand for visual use (like displaying Visa logo or label), that is fine. But if your code logic depends on it, then don't use regex, and don't use 3rd party plugin/library!

Regex detecting card numbers is quick & easy. But in the long run, your project will run into many serious & hard-to-solve bugs. Card issuers keep introducing new card number patterns, or withdraw old ones, or may completely close down. Who knows.


Solution

Build your own solution (preferably non-regex) based on some official pages that's frequently updated, like this page on wikipedia.

As for the "-", ".", "space", and all other noise, simply remove all these non-digits, you can use this (Based on this answer):

$number = preg_replace("/[^0-9]/", "", "4111-1111 1111.1111");
// Output: 4111111111111111

Not convinced yet?

This page goes into deep technical details why regex is hell. (Notice the artical used the word "hell" because once you're in you can't go out)

EDIT

Here's a solution I developed (in PHP):

// Based on https://en.wikipedia.org/wiki/Payment_card_number
// This constant is used in get_card_brand()
// Note: We're not using regex anymore, with this approach way we can easily read/write/change bin series in this array for future changes
// Key     (string)           brand, keep it unique in the array
// Value   (array)            for each element in the array:
//   Key   (string)           prefix of card number, minimum 1 digit maximum 6 digits per prefix. You can use "dash" for range. Example: "34" card number starts with 34. Range Example: "34-36" (which means first 6 digits starts with 340000-369999) card number starts with 34, 35 or 36
//   Value (array of strings) valid length of card number. You can set multiple ones. You can also use "dash" for range. Example: "16" means length must be 16 digits. Range Example: "15-17" length must be 15, 16 or 17. Multiple values example: ["12", "15-17"] card number can be 12 or 15 or 16 or 17 digits
define('CARD_NUMBERS', [
    'american_express' => [
        '34' => ['15'],
        '37' => ['15'],
    ],
    'diners_club' => [
        '36'      => ['14-19'],
        '300-305' => ['16-19'],
        '3095'    => ['16-19'],
        '38-39'   => ['16-19'],
    ],
    'jcb' => [
        '3528-3589' => ['16-19'],
    ],
    'discover' => [
        '6011'          => ['16-19'],
        '622126-622925' => ['16-19'],
        '624000-626999' => ['16-19'],
        '628200-628899' => ['16-19'],
        '64'            => ['16-19'],
        '65'            => ['16-19'],
    ],
    'dankort' => [
        '5019' => ['16'],
        //'4571' => ['16'],// Co-branded with Visa, so it should appear as Visa
    ],
    'maestro' => [
        '6759'   => ['12-19'],
        '676770' => ['12-19'],
        '676774' => ['12-19'],
        '50'     => ['12-19'],
        '56-69'  => ['12-19'],
    ],
    'mastercard' => [
        '2221-2720' => ['16'],
        '51-55'     => ['16'],
    ],
    'unionpay' => [
        '81' => ['16'],// Treated as Discover cards on Discover network
    ],
    'visa' => [
        '4' => ['13-19'],// Including related/partner brands: Dankort, Electron, etc. Note: majority of Visa cards are 16 digits, few old Visa cards may have 13 digits, and Visa is introducing 19 digits cards
    ],
]);

/**
 * Pass card number and it will return brand if found
 * Examples:
 *     get_card_brand('4111111111111111');                    // Output: "visa"
 *     get_card_brand('4111.1111 1111-1111');                 // Output: "visa" function will remove following noises: dot, space and dash
 *     get_card_brand('411111######1111');                    // Output: "visa" function can handle hashed card numbers
 *     get_card_brand('41');                                  // Output: "" because invalid length
 *     get_card_brand('41', false);                           // Output: "visa" because we told function to not validate length
 *     get_card_brand('987', false);                          // Output: "" no match found
 *     get_card_brand('4111 1111 1111 1111 1111 1111');       // Output: "" no match found
 *     get_card_brand('4111 1111 1111 1111 1111 1111', false);// Output: "visa" because we told function to not validate length
 * Implementation Note: This function doesn't use regex, instead it compares digit by digit. 
 *                      Because we're not using regex in this function, it's easier to add/edit/delete new bin series to global constant CARD_NUMBERS
 * Performance Note: This function is extremely fast, less than 0.0001 seconds
 * @param  String|Int $cardNumber     (required) Card number to know its brand. Examples: 4111111111111111 or 4111 1111-1111.1111 or 411111###XXX1111
 * @param  Boolean    $validateLength (optional) If true then will check length of the card which must be correct. If false then will not check length of the card. For example you can pass 41 with $validateLength = false still this function will return "visa" correctly
 * @return String                                returns card brand if valid, otherwise returns empty string
 */
function get_card_brand($cardNumber, $validateLength = true) {
    $foundCardBrand = '';
    
    $cardNumber = (string)$cardNumber;
    $cardNumber = str_replace(['-', ' ', '.'], '', $cardNumber);// Trim and remove noise
    
    if(in_array(substr($cardNumber, 0, 1), ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9'])) {// Try to find card number only if first digit is a number, if not then there is no need to check
        $cardNumber = preg_replace('/[^0-9]/', '0', $cardNumber);// Set all non-digits to zero, like "X" and "#" that maybe used to hide some digits
        $cardNumber = str_pad($cardNumber, 6, '0', STR_PAD_RIGHT);// If $cardNumber passed is less than 6 digits, will append 0s on right to make it 6
        
        $firstSixDigits   = (int)substr($cardNumber, 0, 6);// Get first 6 digits
        $cardNumberLength = strlen($cardNumber);// Total digits of the card
        
        foreach(CARD_NUMBERS as $brand => $rows) {
            foreach($rows as $prefix => $lengths) {
                $prefix    = (string)$prefix;
                $prefixMin = 0;
                $prefixMax = 0;
                if(strpos($prefix, '-') !== false) {// If "dash" exist in prefix, then this is a range of prefixes
                    $prefixArray = explode('-', $prefix);
                    $prefixMin = (int)str_pad($prefixArray[0], 6, '0', STR_PAD_RIGHT);
                    $prefixMax = (int)str_pad($prefixArray[1], 6, '9', STR_PAD_RIGHT);
                } else {// This is fixed prefix
                    $prefixMin = (int)str_pad($prefix, 6, '0', STR_PAD_RIGHT);
                    $prefixMax = (int)str_pad($prefix, 6, '9', STR_PAD_RIGHT);
                }

                $isValidPrefix = $firstSixDigits >= $prefixMin && $firstSixDigits <= $prefixMax;// Is string starts with the prefix

                if($isValidPrefix && !$validateLength) {
                    $foundCardBrand = $brand;
                    break 2;// Break from both loops
                }
                if($isValidPrefix && $validateLength) {
                    foreach($lengths as $length) {
                        $isValidLength = false;
                        if(strpos($length, '-') !== false) {// If "dash" exist in length, then this is a range of lengths
                            $lengthArray = explode('-', $length);
                            $minLength = (int)$lengthArray[0];
                            $maxLength = (int)$lengthArray[1];
                            $isValidLength = $cardNumberLength >= $minLength && $cardNumberLength <= $maxLength;
                        } else {// This is fixed length
                            $isValidLength = $cardNumberLength == (int)$length;
                        }
                        if($isValidLength) {
                            $foundCardBrand = $brand;
                            break 3;// Break from all 3 loops
                        }
                    }
                }
            }
        }
    }
    
    return $foundCardBrand;
}
孤凫 2025-01-13 02:39:54

接受的答案很好,但为了适应新的万事达卡 BIN,我相信它需要更新为:

^(?:4[0-9]{12}(?:[0-9]{3})?|[25][1-7][0-9]{14}|6(? :011|5[0-9][0-9])[0-9]{12} |3[47][0-9]{13}|3(?:0[0-5]|[68][0-9])[0-9]{11}|(?:2131|1800| 35\d{3})\d{11})$

(关键部分是 [25][1-7][0-9]{14},因为第一个数字现在可以是 2 或 5,第二个数字最多可以是7)

如有错误,请指正!

The accepted answer is great, but to accommodate the new MasterCard BIN, I believe that it would need to be updated to:

^(?:4[0-9]{12}(?:[0-9]{3})?|[25][1-7][0-9]{14}|6(?:011|5[0-9][0-9])[0-9]{12}|3[47][0-9]{13}|3(?:0[0-5]|[68][0-9])[0-9]{11}|(?:2131|1800|35\d{3})\d{11})$

(the critical piece being [25][1-7][0-9]{14}, since the first digit can now be either a 2 or a 5 and the second digit can be up to 7)

Please correct me if I'm wrong!

马蹄踏│碎落叶 2025-01-13 02:39:54

对于 Rupay 借记卡:^6[0-9]{15}$

For Rupay Debit Card: ^6[0-9]{15}$

梦中的蝴蝶 2025-01-13 02:39:54

这是我检测卡网络的方法(2020年更新):

function getCardBrandId($pan)
    {
        $regs = [
            ELECTRON => "/^(4026|417500|4405|4508|4844|4913|4917)\d+$/",
            MAESTRO  => "/^(?:50|5[6-9]|6[0-9])\d+$/",
            DANKORT  => "/^(5019|4571)\d+$/",
            CUP      => "/^(62|81)\d+$/",
            VISA     => "/^4[0-9]\d+$/",
            DINERS   => "/^(?:5[45]|36|30[0-5]|3095|3[8-9])\d+$/",
            MC       => "/^(?:5[1-5]|222[1-9]|22[3-9][0-9]|2[3-6][0-9][0-9]|27[0-1][0-9]|2720)\d+$/",
            AMEX     => "/^(34|37)\d+$/",
            DISCOVER => "/^6(?:011|22(12[6-9]|1[3-9][0-9]|[2-8][0-9][0-9]|9[01][0-9]|92[0-5])|5|4|2[4-6][0-9]{3}|28[2-8][0-9]{2})\d+$/",
            JCB      => "/^(?:35[2-8][0-9])\d+$/",
            INTERPAY => "/^(636)\d+$/",
            KOREAN   => "/^9[0-9]\d+$/",
            MIR      => "/^(?:220[0-4])\d+$/",
        ];


        foreach ($regs as $brand => $reg) {
            if (preg_match($reg, $pan)) {
                return $brand;
            }
        }

        return "Unknown";
    }

Here is my method for detecting card network (updated 2020):

function getCardBrandId($pan)
    {
        $regs = [
            ELECTRON => "/^(4026|417500|4405|4508|4844|4913|4917)\d+$/",
            MAESTRO  => "/^(?:50|5[6-9]|6[0-9])\d+$/",
            DANKORT  => "/^(5019|4571)\d+$/",
            CUP      => "/^(62|81)\d+$/",
            VISA     => "/^4[0-9]\d+$/",
            DINERS   => "/^(?:5[45]|36|30[0-5]|3095|3[8-9])\d+$/",
            MC       => "/^(?:5[1-5]|222[1-9]|22[3-9][0-9]|2[3-6][0-9][0-9]|27[0-1][0-9]|2720)\d+$/",
            AMEX     => "/^(34|37)\d+$/",
            DISCOVER => "/^6(?:011|22(12[6-9]|1[3-9][0-9]|[2-8][0-9][0-9]|9[01][0-9]|92[0-5])|5|4|2[4-6][0-9]{3}|28[2-8][0-9]{2})\d+$/",
            JCB      => "/^(?:35[2-8][0-9])\d+$/",
            INTERPAY => "/^(636)\d+$/",
            KOREAN   => "/^9[0-9]\d+$/",
            MIR      => "/^(?:220[0-4])\d+$/",
        ];


        foreach ($regs as $brand => $reg) {
            if (preg_match($reg, $pan)) {
                return $brand;
            }
        }

        return "Unknown";
    }
几味少女 2025-01-13 02:39:54

领先卡网络

万事达卡的正则​​表达式(2-Bin、5-Bin 均):"(?:5[1-5][0-9]{2}|222[1-9] |22[3-9][0-9]|2[3-6][0-9]{2}|27[01][0-9]|2720)[0-9]{12}"

签证:“^4[0-9]{6,}$”

大莱俱乐部:“(^30[0-5][0-9]{11} $)|(^(36|38)[0-9]{12}$)"

美国运通:"^[34|37][0-9]{14}$"

JCB: "(^3[0-9]{15}$)|(^(2131|1800)[0-9]{11}$)"

发现: "^6011-?\d{ 4}-?\d{4}-?\d{4}$"

Regex for Leading Card Networks

Master Card(2-Bin, 5-Bin both):"(?:5[1-5][0-9]{2}|222[1-9]|22[3-9][0-9]|2[3-6][0-9]{2}|27[01][0-9]|2720)[0-9]{12}"

Visa: "^4[0-9]{6,}$"

Diner's Club: "(^30[0-5][0-9]{11}$)|(^(36|38)[0-9]{12}$)"

American Express: "^[34|37][0-9]{14}$"

JCB: "(^3[0-9]{15}$)|(^(2131|1800)[0-9]{11}$)"

Discover: "^6011-?\d{4}-?\d{4}-?\d{4}$"

铁轨上的流浪者 2025-01-13 02:39:54

除了上述内容之外,这里还有一个新万事达卡的正则表达式,其中包括 2221-2720 BIN:

^5[1-5][0-9]{0,14}|^(222[1-9]|2[3-6]\\d{2}|27[0-1]\\d|2720)[0-9]{0,12}

注意,如果用户开始输入与万事达卡相对应的卡数字,则此正则表达式将匹配。例如,如果用户输入“222185”,则正则表达式将匹配,因为没有其他类型的卡以“2221”开头。如果您想在输入卡的第一个数字时显示卡类型,则此正则表达式可能会派上用场。

或者,如果您想要“事后”匹配,您可以更改 {0,14}{0,12} 的最后一部分到 {14}{12}

^5[1-5][0-9]{14}|^(222[1-9]|2[3-6]\\d{2}|27[0-1]\\d|2720)[0-9]{12}

In addition to all above, here's a regex for new MasterCards, that includes 2221-2720 BINs:

^5[1-5][0-9]{0,14}|^(222[1-9]|2[3-6]\\d{2}|27[0-1]\\d|2720)[0-9]{0,12}

Note, this regex will match if user starts typing card digits, that correspond to MasterCard. For example, if user types "222185" then the regex will match, because there is no other type of card that starts with "2221". This regex might come handy if you want to display card type while typing first digits of the card.

Alternatively, if you want "post factum" matching, you can change the last part from {0,14} and {0,12} to {14} and {12}:

^5[1-5][0-9]{14}|^(222[1-9]|2[3-6]\\d{2}|27[0-1]\\d|2720)[0-9]{12}
究竟谁懂我的在乎 2025-01-13 02:39:54

我想出了一个允许使用破折号和空格的正则表达式。在这里测试一下: https://regex101.com/r/Rx2iWD/1

允许逗号(我认为这很不寻常),只需将其添加到 sep 定义中即可。

在 PHP 中:

$ccPatt = '/
    (?(DEFINE)
        (?<sep> [ -]?)
    )
    (?<!\d)(?:
      \d{4} (?&sep) \d{4} (?&sep) \d{4} (?&sep) \d{4}               # 16 digits
    | \d{3} (?&sep) \d{3} (?&sep) \d{3} (?&sep) \d (?&sep) \d{3}    # 13 digits
    | \d{4} (?&sep) \d{6} (?&sep) \d{4}                             # 14 digits
    | \d{4} (?&sep) \d{6} (?&sep) \d{5}                             # 15 digit card
    )(?!\d)
/xu';

I came up with a regex that allows for dashes and spaces. Test it here: https://regex101.com/r/Rx2iWD/1

To allow commas (which I think is unusual), just add it to the sep definition.

In PHP:

$ccPatt = '/
    (?(DEFINE)
        (?<sep> [ -]?)
    )
    (?<!\d)(?:
      \d{4} (?&sep) \d{4} (?&sep) \d{4} (?&sep) \d{4}               # 16 digits
    | \d{3} (?&sep) \d{3} (?&sep) \d{3} (?&sep) \d (?&sep) \d{3}    # 13 digits
    | \d{4} (?&sep) \d{6} (?&sep) \d{4}                             # 14 digits
    | \d{4} (?&sep) \d{6} (?&sep) \d{5}                             # 15 digit card
    )(?!\d)
/xu';
谁许谁一生繁华 2025-01-13 02:39:54

对于任何尝试使用 Swift (iOS) 执行此操作的人,我构建了一个不使用 RegEx 的小项目,该项目执行 CC 前缀验证、检查数字验证(使用 Luhn 算法)以及其他一些很酷的功能事物。修改添加新的卡类型和号码范围非常简单,无需了解复杂的正则表达式。这与 @evilReiko 在他的回答中所做的类似。

100%免费。完全开源。

https://github.com/ethanwa/credit-card-scanner-and-验证器

For anyone trying to do this with Swift (iOS), I built a little project that doesn't use RegEx that does CC prefix validation, check digit validation (using Luhn algorithm), and a few other cool things. It is very simple to modify to add new card types and number ranges without having to know complex RegEx. It's similar to what @evilReiko does in his answer.

100% free. Full open source.

https://github.com/ethanwa/credit-card-scanner-and-validator

Oo萌小芽oO 2025-01-13 02:39:54

Rupay 卡的 Regx :

(508[5-9][0-9]{12})|(6069[8-9][0-9]{11})|(607[0-8][0-9]{12}) |(6079[0-8][0-9]{11})|(608[0- 5][0-9]{12})|(6521[5-9][0-9]{11})|(652[2-9][0-9]{12})|(6530[0 -9]{12})|(6531[0-4][0-9]{11})

使用bin系列:
508500 – 508999,
606985 – 606999,
607000 - 607899,
607900 - 607984,
608001 -- 608500,
652150 --- 652199,
652200 --- 652999,
653000 --- 653099,
653100 --- 653149,

Regx for Rupay card :

(508[5-9][0-9]{12})|(6069[8-9][0-9]{11})|(607[0-8][0-9]{12})|(6079[0-8][0-9]{11})|(608[0-5][0-9]{12})|(6521[5-9][0-9]{11})|(652[2-9][0-9]{12})|(6530[0-9]{12})|(6531[0-4][0-9]{11})

using bin series :
508500 – 508999,
606985 – 606999,
607000 - 607899,
607900 - 607984,
608001 -- 608500,
652150 --- 652199,
652200 --- 652999,
653000 --- 653099,
653100 --- 653149,

夜司空 2025-01-13 02:39:54

所有卡类型的正则表达式

^(3[47][0-9]{13}|(6541|6556)[0-9]{12}|389[0-9]{11}|3(?:0[0-5]|[68][0-9])[0-9]{11}|65[4-9][0-9]{13}|64[4-9][0-9]{13}|6011[0-9]{12}|(622(?:12[6-9]|1[3-9][0-9]|[2-8][0-9][0-9]|9[01][0-9]|92[0-5])[0-9]{10})|63[7-9][0-9]{13}|(?:2131|1800|35\d{3})\d{11}|9[0-9]{15}|(6304|6706|6709|6771)[0-9]{12,15}|(5018|5020|5038|6304|6759|6761|6763)[0-9]{8,15}|(5[1-5][0-9]{14}|2(22[1-9][0-9]{12}|2[3-9][0-9]{13}|[3-6][0-9]{14}|7[0-1][0-9]{13}|720[0-9]{12}))|(6334|6767)[0-9]{12}|(6334|6767)[0-9]{14}|(6334|6767)[0-9]{15}|(4903|4905|4911|4936|6333|6759)[0-9]{12}|(4903|4905|4911|4936|6333|6759)[0-9]{14}|(4903|4905|4911|4936|6333|6759)[0-9]{15}|564182[0-9]{10}|564182[0-9]{12}|564182[0-9]{13}|633110[0-9]{10}|633110[0-9]{12}|633110[0-9]{13}|(62[0-9]{14,17})|4[0-9]{12}(?:[0-9]{3})?|(?:4[0-9]{12}(?:[0-9]{3})?|5[1-5][0-9]{14}))$

检查可以在这里
https://regex101.com/r/37S1iV/1

Regex for all card type

^(3[47][0-9]{13}|(6541|6556)[0-9]{12}|389[0-9]{11}|3(?:0[0-5]|[68][0-9])[0-9]{11}|65[4-9][0-9]{13}|64[4-9][0-9]{13}|6011[0-9]{12}|(622(?:12[6-9]|1[3-9][0-9]|[2-8][0-9][0-9]|9[01][0-9]|92[0-5])[0-9]{10})|63[7-9][0-9]{13}|(?:2131|1800|35\d{3})\d{11}|9[0-9]{15}|(6304|6706|6709|6771)[0-9]{12,15}|(5018|5020|5038|6304|6759|6761|6763)[0-9]{8,15}|(5[1-5][0-9]{14}|2(22[1-9][0-9]{12}|2[3-9][0-9]{13}|[3-6][0-9]{14}|7[0-1][0-9]{13}|720[0-9]{12}))|(6334|6767)[0-9]{12}|(6334|6767)[0-9]{14}|(6334|6767)[0-9]{15}|(4903|4905|4911|4936|6333|6759)[0-9]{12}|(4903|4905|4911|4936|6333|6759)[0-9]{14}|(4903|4905|4911|4936|6333|6759)[0-9]{15}|564182[0-9]{10}|564182[0-9]{12}|564182[0-9]{13}|633110[0-9]{10}|633110[0-9]{12}|633110[0-9]{13}|(62[0-9]{14,17})|4[0-9]{12}(?:[0-9]{3})?|(?:4[0-9]{12}(?:[0-9]{3})?|5[1-5][0-9]{14}))$

Check can here
https://regex101.com/r/37S1iV/1

清秋悲枫 2025-01-13 02:39:54

First Data 验证 Amex 的 15 位数字和 Visa、mc、discover、diners 和 jcb 的 16 位数字,因此我仅在卡号长度为 15 或 16 位时才将卡号发送给他们,使用以下方法:

^[0-9]{15}(?:[0-9]{1})?$

First Data validates 15 digits for Amex and 16 for visa, mc, discover, diners, and jcb so I only send the card number to them if the number is 15 or 16 digits long using this:

^[0-9]{15}(?:[0-9]{1})?$
维持三分热 2025-01-13 02:39:54

这是
此答案格式为 JavaScript 代码。

  let card_types = {
    "Amex Card": /^3[47][0-9]{13}$/,
    "BCGlobal": /^(6541|6556)[0-9]{12}$/,
    "Carte Blanche Card": /^389[0-9]{11}$/,
    "Diners Club Card": /^3(?:0[0-5]|[68][0-9])[0-9]{11}$/,
    "Discover Card": /^65[4-9][0-9]{13}|64[4-9][0-9]{13}|6011[0-9]{12}|(622(?:12[6-9]|1[3-9][0-9]|[2-8][0-9][0-9]|9[01][0-9]|92[0-5])[0-9]{10})$/,
    "Insta Payment Card": /^63[7-9][0-9]{13}$/,
    "JCB Card": /^(?:2131|1800|35\d{3})\d{11}$/,
    "KoreanLocalCard": /^9[0-9]{15}$/,
    "Laser Card": /^(6304|6706|6709|6771)[0-9]{12,15}$/,
    "Maestro Card": /^(5018|5020|5038|6304|6759|6761|6763)[0-9]{8,15}$/,
    "Mastercard":/ ^(5[1-5][0-9]{14}|2(22[1-9][0-9]{12}|2[3-9][0-9]{13}|[3-6][0-9]{14}|7[0-1][0-9]{13}|720[0-9]{12}))$/,
    "Solo Card": /^(6334|6767)[0-9]{12}|(6334|6767)[0-9]{14}|(6334|6767)[0-9]{15}$/,
    "Switch Card": /^(4903|4905|4911|4936|6333|6759)[0-9]{12}|(4903|4905|4911|4936|6333|6759)[0-9]{14}|(4903|4905|4911|4936|6333|6759)[0-9]{15}|564182[0-9]{10}|564182[0-9]{12}|564182[0-9]{13}|633110[0-9]{10}|633110[0-9]{12}|633110[0-9]{13}$/,
    "Union Pay Card": /^(62[0-9]{14,17})$/,
    "Visa Card": /^4[0-9]{12}(?:[0-9]{3})?$/,
    "Visa Master Card": /^(?:4[0-9]{12}(?:[0-9]{3})?|5[1-5][0-9]{14})$/,
  };

Here's
this answer formatted as JavaScript code.

  let card_types = {
    "Amex Card": /^3[47][0-9]{13}$/,
    "BCGlobal": /^(6541|6556)[0-9]{12}$/,
    "Carte Blanche Card": /^389[0-9]{11}$/,
    "Diners Club Card": /^3(?:0[0-5]|[68][0-9])[0-9]{11}$/,
    "Discover Card": /^65[4-9][0-9]{13}|64[4-9][0-9]{13}|6011[0-9]{12}|(622(?:12[6-9]|1[3-9][0-9]|[2-8][0-9][0-9]|9[01][0-9]|92[0-5])[0-9]{10})$/,
    "Insta Payment Card": /^63[7-9][0-9]{13}$/,
    "JCB Card": /^(?:2131|1800|35\d{3})\d{11}$/,
    "KoreanLocalCard": /^9[0-9]{15}$/,
    "Laser Card": /^(6304|6706|6709|6771)[0-9]{12,15}$/,
    "Maestro Card": /^(5018|5020|5038|6304|6759|6761|6763)[0-9]{8,15}$/,
    "Mastercard":/ ^(5[1-5][0-9]{14}|2(22[1-9][0-9]{12}|2[3-9][0-9]{13}|[3-6][0-9]{14}|7[0-1][0-9]{13}|720[0-9]{12}))$/,
    "Solo Card": /^(6334|6767)[0-9]{12}|(6334|6767)[0-9]{14}|(6334|6767)[0-9]{15}$/,
    "Switch Card": /^(4903|4905|4911|4936|6333|6759)[0-9]{12}|(4903|4905|4911|4936|6333|6759)[0-9]{14}|(4903|4905|4911|4936|6333|6759)[0-9]{15}|564182[0-9]{10}|564182[0-9]{12}|564182[0-9]{13}|633110[0-9]{10}|633110[0-9]{12}|633110[0-9]{13}$/,
    "Union Pay Card": /^(62[0-9]{14,17})$/,
    "Visa Card": /^4[0-9]{12}(?:[0-9]{3})?$/,
    "Visa Master Card": /^(?:4[0-9]{12}(?:[0-9]{3})?|5[1-5][0-9]{14})$/,
  };
〆一缕阳光ご 2025-01-13 02:39:54

对于评论中要求解决空格分隔值的多个请求,这可以很容易地实现:

\b(\d{4}\s\d{4}\s\d{4}\s\d{4}$)\b

For the multiple requests in the comments asking for a solution to whitespace separated values, this can be achieved pretty easily:

\b(\d{4}\s\d{4}\s\d{4}\s\d{4}$)\b
淡笑忘祈一世凡恋 2025-01-13 02:39:54

javascript中的evilReiko解决方案

var CARD_NUMBERS = {
'american_express': {
    '34': ['15'],
    '37': ['15'],
},
'diners_club': {
    '36'     : ['14-19'],
    '300-305': ['16-19'],
    '3095'   : ['16-19'],
    '38-39'  : ['16-19'],
},
'jcb': {
    '3528-3589': ['16-19'],
},
'discover': {
    '6011'         : ['16-19'],
    '622126-622925': ['16-19'],
    '624000-626999': ['16-19'],
    '628200-628899': ['16-19'],
    '64'           : ['16-19'],
    '65'           : ['16-19'],
},
'dankort': {
    '5019': ['16'],
},
'maestro': {
    '6759'  : ['12-19'],
    '676770': ['12-19'],
    '676774': ['12-19'],
    '50'    : ['12-19'],
    '56-69' : ['12-19'],
},
'mastercard': {
    '2221-2720': ['16'],
    '51-55'    : ['16'],
},
'unionpay': {
    '81': ['16'],
},
'visa': {
    '4': ['13-19'],
}
};


var get_card_brand = function(cardNumber, validateLength = true)
{
cardNumber      = (cardNumber || "").toString().replace(new RegExp("[-.\\s]", "gmi"), "").trim();

if(/[0-9]/.test(cardNumber.substr(0,1)))
{
    cardNumber = cardNumber.replace(new RegExp("[^0-9]", "gmi"), "0").padEnd(6, "0");

    var firstSixDigits   = parseInt(cardNumber.substr(0, 6));
    var cardNumberLength = cardNumber.length;

    for(var brand in CARD_NUMBERS)
    {
        for(var prefix in CARD_NUMBERS[brand])
        {
            var lengths = CARD_NUMBERS[brand][prefix];

            var prefixArray = prefix.split("-");

            var prefixMin = parseInt(prefixArray[0].padEnd(6, "0"));
            var prefixMax = parseInt((prefixArray[1] || prefixArray[0]).padEnd(6, "9"));

            var isValidPrefix = firstSixDigits >= prefixMin && firstSixDigits <= prefixMax;

            if(isValidPrefix && !validateLength)
            {
                return brand;
            }


            if(isValidPrefix && validateLength)
            {
                for(var i = 0; i < lengths.length; i++)
                {
                    var length = lengths[i];
                    var isValidLength = false;

                    var lengthArray = length.split("-");

                    var minLength = parseInt(lengthArray[0]);
                    var maxLength = parseInt(lengthArray[1] || lengthArray[0]);

                    var isValidLength = cardNumberLength >= minLength && cardNumberLength <= maxLength;

                    if(isValidLength)
                    {
                        return brand;
                    }
                }
            }
        }
    }
}

return "";
}


console.log(get_card_brand('4111111111111111') == "visa");                    // Output: "visa"
console.log(get_card_brand('4111.1111 1111-1111') == "visa");                 // Output: "visa" function will remove following noises: dot, space and dash
console.log(get_card_brand('411111######1111') == "visa");                    // Output: "visa" function can handle hashed card numbers
console.log(get_card_brand('41') == "");                                  // Output: "" because invalid length
console.log(get_card_brand('41', false) == "visa");                           // Output: "visa" because we told function to not validate length
console.log(get_card_brand('987', false) == "");                          // Output: "" no match found
console.log(get_card_brand('4111 1111 1111 1111 1111 1111') == "");       // Output: "" no match found
console.log(get_card_brand('4111 1111 1111 1111 1111 1111', false) == "visa");// Output: "visa" because we told fu**/

evilReiko solution in javascript

var CARD_NUMBERS = {
'american_express': {
    '34': ['15'],
    '37': ['15'],
},
'diners_club': {
    '36'     : ['14-19'],
    '300-305': ['16-19'],
    '3095'   : ['16-19'],
    '38-39'  : ['16-19'],
},
'jcb': {
    '3528-3589': ['16-19'],
},
'discover': {
    '6011'         : ['16-19'],
    '622126-622925': ['16-19'],
    '624000-626999': ['16-19'],
    '628200-628899': ['16-19'],
    '64'           : ['16-19'],
    '65'           : ['16-19'],
},
'dankort': {
    '5019': ['16'],
},
'maestro': {
    '6759'  : ['12-19'],
    '676770': ['12-19'],
    '676774': ['12-19'],
    '50'    : ['12-19'],
    '56-69' : ['12-19'],
},
'mastercard': {
    '2221-2720': ['16'],
    '51-55'    : ['16'],
},
'unionpay': {
    '81': ['16'],
},
'visa': {
    '4': ['13-19'],
}
};


var get_card_brand = function(cardNumber, validateLength = true)
{
cardNumber      = (cardNumber || "").toString().replace(new RegExp("[-.\\s]", "gmi"), "").trim();

if(/[0-9]/.test(cardNumber.substr(0,1)))
{
    cardNumber = cardNumber.replace(new RegExp("[^0-9]", "gmi"), "0").padEnd(6, "0");

    var firstSixDigits   = parseInt(cardNumber.substr(0, 6));
    var cardNumberLength = cardNumber.length;

    for(var brand in CARD_NUMBERS)
    {
        for(var prefix in CARD_NUMBERS[brand])
        {
            var lengths = CARD_NUMBERS[brand][prefix];

            var prefixArray = prefix.split("-");

            var prefixMin = parseInt(prefixArray[0].padEnd(6, "0"));
            var prefixMax = parseInt((prefixArray[1] || prefixArray[0]).padEnd(6, "9"));

            var isValidPrefix = firstSixDigits >= prefixMin && firstSixDigits <= prefixMax;

            if(isValidPrefix && !validateLength)
            {
                return brand;
            }


            if(isValidPrefix && validateLength)
            {
                for(var i = 0; i < lengths.length; i++)
                {
                    var length = lengths[i];
                    var isValidLength = false;

                    var lengthArray = length.split("-");

                    var minLength = parseInt(lengthArray[0]);
                    var maxLength = parseInt(lengthArray[1] || lengthArray[0]);

                    var isValidLength = cardNumberLength >= minLength && cardNumberLength <= maxLength;

                    if(isValidLength)
                    {
                        return brand;
                    }
                }
            }
        }
    }
}

return "";
}


console.log(get_card_brand('4111111111111111') == "visa");                    // Output: "visa"
console.log(get_card_brand('4111.1111 1111-1111') == "visa");                 // Output: "visa" function will remove following noises: dot, space and dash
console.log(get_card_brand('411111######1111') == "visa");                    // Output: "visa" function can handle hashed card numbers
console.log(get_card_brand('41') == "");                                  // Output: "" because invalid length
console.log(get_card_brand('41', false) == "visa");                           // Output: "visa" because we told function to not validate length
console.log(get_card_brand('987', false) == "");                          // Output: "" no match found
console.log(get_card_brand('4111 1111 1111 1111 1111 1111') == "");       // Output: "" no match found
console.log(get_card_brand('4111 1111 1111 1111 1111 1111', false) == "visa");// Output: "visa" because we told fu**/

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文