65

正規表現パターンがクレジットカードと一致する1つのアプリケーションをテストしているので、そのような番号を強調表示する必要があります。テスト用のテストクレジットカード番号を作成するために、サイトhttp://regexpal.com/を使用しています。私の要件は、それらの間に「-」および/または「、」を含むことができる有効なクレジットカード番号を持っていることです。サイトを使用してテストしたときのような番号を作成することに成功しませんでした

http://regexpal.com

以下のシナリオでいくつかのクレジット番号が必要です

  1. 任意の桁の間に「-」を含めることができる有効なクレジットカード番号。
  2. 任意の桁の間に「、」を含めることができる有効なクレジットカード番号。
  3. 任意の桁の間に「、」または「-」の組み合わせを持つことができる有効なクレジットカード番号。
4

14 に答える 14

212

一般的なクレジットカードベンダーの正規表現:

  • アメックスカード:^3[47][0-9]{13}$
  • BCGlobal:^(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}$
  • 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|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})?$
  • Visaマスターカード:^(?:4[0-9]{12}(?:[0-9]{3})?|5[1-5][0-9]{14})$
于 2014-04-22T22:21:17.697 に答える
77

,最初に文字列からすべておよび-その他の非数字を削除します。

次に、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})$

于 2012-02-16T17:08:42.073 に答える
22

2019年

行う。いいえ。使用する。正規表現!!! (3つの感嘆符付き)


コメントから、 PeteWiFiのコメントを強調する必要があります。

ただ友好的な警告ですが、この方法で特定のスキームとカードの長さを一致させようとすると、あなたは傷ついた世界にいます。たとえば、Switchは2002年以来存在せず、Laserは2014年に撤回され、Visaは19桁のカードを発行する予定であり、MasterCardは現在2xxxxxの範囲で発行されており、このアプローチのいくつかの問題を強調しています。正規表現は、基本的な「カード番号のように見えますか」には適していますが、それをはるかに超えるものではありません。

視覚的に使用するカードのブランド(Visaのロゴやラベルの表示など)を知るためだけに正規表現を使用したい場合は、それで問題ありません。ただし、コードロジックがそれに依存している場合は、正規表現を使用したり、サードパーティのプラグイン/ライブラリを使用したりしないでください。

カード番号を検出する正規表現はすばやく簡単です(とても魅力的です、私は知っています!)。しかし、長期的には、プロジェクトは多くの深刻で解決が難しいバグに遭遇します。カード発行者は、新しいカード番号パターンを導入し続けるか、古いカード番号パターンを撤回するか、完全に閉鎖する可能性があります。知るか。


解決

ウィキペディアのこのページのように、頻繁に更新されるいくつかの公式ページに基づいて、独自のソリューション(できれば非正規表現)を構築します。

「-」、「。」、「スペース」、およびその他すべてのノイズについては、これらの非数字をすべて削除するだけで、これを使用できます(この回答に基づく):

$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-03-06T09:26:13.183 に答える
9

受け入れられた答えは素晴らしいですが、新しいMasterCard 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になり、2番目の桁が最大7になる可能性があるためです)

私が間違っているなら私を訂正してください!

于 2017-03-29T20:32:11.110 に答える
7

Rupayデビットカードの場合:^6[0-9]{15}$

于 2016-01-06T12:30:50.193 に答える
3

上記のすべてに加えて、2221-2720BINを含む新しいMasterCardの正規表現を次に示します。

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

この正規表現は、ユーザーがMasterCardに対応するカードの数字を入力し始めた場合に一致することに注意してください。たとえば、ユーザーが「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}
于 2018-04-18T09:09:53.113 に答える
2

主要なカードネットワークの正規表現

マスターカード(2ビン、5ビン両方): "(?: 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} $)"

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

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

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

于 2017-11-23T08:36:23.250 に答える
2

Swift(iOS)でこれを行おうとしている人のために、CCプレフィックス検証、チェックディジット検証(Luhnアルゴリズムを使用)、およびその他のいくつかの優れた機能を実行するRegExを使用しない小さなプロジェクトを作成しました。複雑な正規表現を知らなくても、新しいカードタイプと番号範囲を追加するように変更するのは非常に簡単です。@evilReikoが彼の答えで行うことと似ています。

100%無料。完全なオープンソース。

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

于 2021-03-20T08:11:45.993 に答える
1

Rupayカードの正規表現:

(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})

ビンシリーズの使用:508500 – 508999、606985 – 606999、607000-607899、607900-607984、608001 --608500、652150 --- 652199、652200 --- 652999、653000 --- 653099、653100 --- 653149、

于 2016-02-10T09:52:53.653 に答える
1

ダッシュとスペースを使用できる正規表現を思いつきました。ここでテストしてください: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';
于 2016-11-10T23:20:19.540 に答える
1

カードネットワークを検出するための私の方法は次のとおりです(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";
    }
于 2020-06-02T09:34:08.423 に答える
0

First DataはAmexで15桁、visa、mc、discover、diners、jcbで16桁を検証するため、カード番号が15桁または16桁の場合にのみ、次のコマンドを使用してカード番号を送信します。

^[0-9]{15}(?:[0-9]{1})?$
于 2017-09-14T17:04:21.797 に答える
0

すべてのカードタイプの正規表現

^(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

于 2021-04-29T11:13:18.330 に答える
0

私は人々がこのタスクのために2021年に正規表現を使用することを本当に迷惑に感じました。ほとんどのクレジットカード番号はLuhnアルゴリズムに従っているため、銀行などに関連する文字を取得し、実際のクレジットカード番号にはこの一般的なソリューションを使用します。この問題を単純にブルートフォースすることはできません。python3のLuhnアルゴリズムをモダンなスタイルでリファクタリングしました。この比較的高速なループを使用する方が適切です。また、パフォーマンスは正規表現よりもおそらく優れています。

    def check_luhn(cardNo: str) -> bool:
        res = 0
         
        for i, digit in enumerate(cardNo[::-1]):
            d = ord(digit) - ord('0')

            # multiply if it's a second digit
            d = d * 2 if i % 2 == 1 else d

            # if digit get's higher than 10 sum the resulting digits
            res += d // 10 + d % 10
         
        return res % 10 == 0
于 2021-08-04T01:08:51.060 に答える