这是我要解决的难题之一,问题是验证具有以下特征的信用卡号:



它必须精确地包含16位数字。
它必须以4,5或6开头。
它只能由数字(0-9)组成。
它可能有4位一组的数字,中间用一个连字符“-”分隔。
不得使用其他任何分隔符,例如'','_'等。
不得包含4个或更多连续的重复数字。



我在这里和这里也都经历了一些解决方案,但是验证的特征与我正在研究的特征不同,因此将其发布。

total_count = int(raw_input())
numbers_list = []
#refernece list to check the starting series
start_list = [4,5,6]

for count in range(total_count):
    numbers_list.append(raw_input())

#condition 1, validates the starting series
def val_start(num):
    if int(num[0]) in start_list:
        return True
    else:
        return False

#to check if individial elements of the list are of length=4
#4321-5555-67899-9991, splitted to ["4321","5555","67899","991"] which is invalid
def val_group(num):
    for val in num.split("-"):
        if len(val) != 4:
            return False

    return True

#condition 2, validates the length of the number
def val_len(num):
    check = num
    check2 = True
    if "-" in num:
        check = "".join(num.split("-"))
        check2 = val_group(num)

    if ((len(check) == 16) and check2):
        return True
    else:
        return False


#condition 3, validates if input consists only number
def val_isdigit(num):
    if not num.isdigit():
        for ch in num:
            if not (ch.isdigit() | (ch == "-")):
                return False
    return True

#condition 4, validates the repetition of any number for 4 consective times
def val_rep(num):
    res = "".join(num.split("-"))
    for i in range(len(res)):
        try:
            if (res[i] == res[i+1]):
                if (res[i+1] == res[i+2]):
                    if (res[i+2] == res[i+3]):
                        return False
        except IndexError:
           pass
    return True

for num in numbers_list:
    #returns all the values into a list
    result = [val_start(num), val_len(num),val_isdigit(num), val_rep(num)]
    if False in result:
        print("Invalid")
    else:
        print("Valid")


我正在寻找函数val_rep()的优化方法,该方法可以验证任意数字连续4次重复。

评论

我意识到这只是人为的挑战,但是万一有人好奇,万事达卡很快就会发行以2而不是5开头的卡号,这将使检查失败: /…

除了这些要求之外,我建议您进行luhn检查en.wikipedia.org/wiki/Luhn_algorithm

请不要更新您问题中的代码以合并答案的反馈,因为这样做有悖于“代码审阅”的“问题+答案”风格。这不是一个论坛,您应该在其中保留问题的最新版本。收到答案后,请查看您可能会做什么和可能不会做什么。

好吧..当然..我不知道..

最后一位数字是JeffS在en.wikipedia.org/wiki/ISO/IEC_7812#Check_digit
中提到的mod 10校验和。

#1 楼

首先,您应该将功能封装在一个函数中,除非您确实只想使用一次代码。目前,您只需要检查第二张信用卡就可以复制代码。因此,让我们用一个文档字符串编写一个函数: br />
def is_valid_card_number(sequence):
    """Returns `True' if the sequence is a valid credit card number.

    A valid credit card number
    - must contain exactly 16 digits,
    - must start with a 4, 5 or 6 
    - must only consist of digits (0-9) or hyphens '-',
    - may have digits in groups of 4, separated by one hyphen "-". 
    - must NOT use any other separator like ' ' , '_',
    - must NOT have 4 or more consecutive repeated digits.
    """

    #  do your stuff here


这似乎很不可思议,所以让我们看一下这种模式。括号内的任何内容均视为一个组。因此,我们有四组:([456][0-9]{3})和三倍([0-9]{4})。中间的连字符为?,因此是可选的。如果它们存在,那么它们必须出现一次,因此不允许--

[…]表示字符类。此时仅允许该类内的字符。 ^表示我们正在描述字符串的开始,而不是中间的某个点。因此,我们必须从456开始。现在满足了这个要求。接下来是3({3})个数字。因此,我们的第一组由四个数字组成,其他数字也是如此。 $确保没有剩余:我们的字符串必须到此为止。

re文档提供了更多信息。

我们现在满足了大多数条件:


我们有16位数字
我们有四个数字组
我们只有数字或连字符
我们以4、5或6开头

我们缺少四个连续数字。现在这很棘手,因为不清楚连字符是否是运行的一部分。如果它们仅是四个4个字符组的无效字符,我们可以简单地检查当前字符* 4是否是完整的组:

import re

PATTERN='^([456][0-9]{3})-?([0-9]{4})-?([0-9]{4})-?([0-9]{4})$'


如果重复的数字也不能跨连字符/ groups有点复杂,但又不过分。不过,这只是练习。

话虽如此:请确保处理多个数字,而不是单个数字。这样,您将能够更轻松地检查代码。但这取决于。如果它是一次性脚本,则可能不需要功能。

评论


\ $ \ begingroup \ $
请注意,您的代码说1122-2233-4455-6677或1122223344556677是有效的,但是我不认为通过了“不得具有4个或更多连续的重复数字”的要求
\ $ \ endgroup \ $
– Peilonrayz
17年7月18日在9:01

\ $ \ begingroup \ $
@Peilonrayz在代码的下方指出:“如果重复的数字也可能不跨连字符/组,则它会变得有些复杂,但不会太多。不过,这是一种练习。”
\ $ \ endgroup \ $
– Zeta
17年7月18日在9:02



\ $ \ begingroup \ $
是的,我错过了。 :)
\ $ \ endgroup \ $
– Peilonrayz
17年7月18日在9:05

\ $ \ begingroup \ $
您不能只做PATTERN ='^([456] [0-9] {3})(-?([0-9] {4}){3})$',还是我遗漏了什么?
\ $ \ endgroup \ $
–斯蒂芬·S
17年7月18日在13:00

\ $ \ begingroup \ $
@StephenS现在我们有一个match.groups太多了。您将不得不忽略第二个。 (?:…)可能有效,但是目前我不确定后者是否已在Python中捕获。
\ $ \ endgroup \ $
– Zeta
17年7月18日在13:02



#2 楼

您可以使用正则表达式执行大部分操作。满足您的要求:




它只能由数字(0-9)组成。


因此我们可以为每个数字使用[0-9]



它必须恰好包含16个数字。


所以我们可以使用一些东西像[0-9]{16}一样。但是,那只会检查它是否包含十六位数字,因此1234567890123456abc将是有效的。所以我们需要类似^[0-9]{16}$的东西,它必须以4,5或6开头


,因此我们可以更改第一场比赛。 ^[456][0-9]{15}$



它可能有4位一组的数字,中间用一个连字符“-”分隔。更长一点,但是仍然很简单。唯一复杂的是它看起来是全部还是什么,所以您可以分开还是不分开。最好在Python中进行此检查。但是,这意味着用-?
分组。 。


与上述相同。但是,如果它希望我们允许使用这些分隔符,则必须将分隔符更改为[\- _]之类的组。


我们不能在纯正则表达式中轻松地做到这一点,所以最好在Python中做到这一点。因此,我们要使用捕获组来获取数字。

^[456][0-9]{3}-?[0-9]{4}-?[0-9]{4}-?[0-9]{4}$



因此,如果它与上述正则表达式匹配,则只需检查没有四个连续的数字相同。

为了改进val_rep,我将对itertools配对食谱使用修改后的版本。

^([456][0-9]{3})-?([0-9]{4})-?([0-9]{4})-?([0-9]{4})$


可以进行简单检查,例如:

def quadwise(iterable):
    "s -> (s0,s1,s2,s3), (s1,s2,s3,s4), (s2,s3,s4,s5), ..."
    a, b, c, d = tee(iterable, 4)
    next(b, None)
    next(c, None)
    next(c, None)
    next(d, None)
    next(d, None)
    next(d, None)
    return zip(a, b, c, d)


可以简化为:

def val_rep(num):
    for head, *tail in quadwise(num):
        if all(head == item for item in tail):
            return False
    return True


Python 2中的代码必须是这样的:
def val_rep(num):
    return not any(
        all(head == item for item in tail)
        for head, *tail in quadwise(num)
    )


评论


\ $ \ begingroup \ $
您已经讨论过-?,但是您在代码中遗漏了它。错别字?哦,我们可以在正则表达式中进行4位数连续检查,但是表达式会很丑。
\ $ \ endgroup \ $
– Zeta
17年7月18日在8:55



\ $ \ begingroup \ $
(您的最后一个非Python代码段仍不存在-?)
\ $ \ endgroup \ $
– Zeta
17年7月18日在8:57



\ $ \ begingroup \ $
@Zeta糟糕,谢谢您。 :)我注意到我在写答案的一半时就读错了。
\ $ \ endgroup \ $
– Peilonrayz
17年7月18日在8:57

\ $ \ begingroup \ $
如果在REGEX中使用^ $,则不需要card.count('-')。
\ $ \ endgroup \ $
– Zeta
17年7月18日在9:05

\ $ \ begingroup \ $
我认为,与itertools.groupby(num)中的group的any(len(group)> = 4)之类的东西相比,四元方法是过大的。
\ $ \ endgroup \ $
–加雷斯·里斯(Gareth Rees)
17年7月18日在16:51

#3 楼

还可以通过使用反向引用使用正则表达式简化val_rep函数(请参阅\number语法)。在这里,将被替换为第一个匹配的数字。
import re

REP_PATTERN=r"([0-9])(?:-?){3}"

def val_rep(sequence):
    return not re.search(REP_PATTERN, sequence)


请注意,在第二种模式中,我们使用反向引用拒绝连字符不一致的卡号,例如(?!...)4123-45678910-1234将匹配连字符或空序列)

#4 楼

您可以使用正则表达式来大大简化此过程。 Python包括一个有效的模块,名为re(正则表达式的缩写)。在这种情况下,请检查给定的字符串是否是有效的信用卡号(CC.py):

import re

PATTERN = "([4-6]{1})([0-9]{3}-?)([0-9]{4}-?){2}([0-9]{4})"


def is_valid_creditcard(sequence):
    """Check if a sequence is a valid credit card number.
    Rules for sequences to qualify as credit card numbers:

    Sequences must:

    -Contain exactly 16 digits;
    -Start with a 4,5 or 6;
    -Only consist of digits (0-9).

    Sequences may:
    -Have digits in groups of 4, separated by one hyphen.

    Sequence must not:
    -Use any other separator;
    -Have 4 or more consecutive repeated digits.
    """
    for i, n in enumerate(sequence):
        try:
            if (sequence[i], 
                sequence[i+1], 
                sequence[i+2],
                sequence[i+3]
            ) == (n, n, n, n):
                return False
        except IndexError:
            pass
    return bool(re.match(PATTERN, sequence))


示例:

>>> import CC
>>> CC.is_valid_creditcard("0000-1111-2222-3333")
False
>>> # Starts with "0", not "4"/ "5" / "6"
>>> CC.is_valid_creditcard("4444-5555-6666-777")
False
>>> # Incorrectly grouped
>>> CC.is_valid_creditcard("4444-3333-2222-XXXX")
False
>>> # Contains non-numerical characters
>>> CC.is_valid_creditcard("444-55555-6666-7777")
False
>>> # Incorrectly grouped
>>> CC.is_valid_creditcard("4567:8901:2345:6789")
False
>>> # Illegal seperators
>>> CC.is_valid_creditcard("4444-5555-6666-7777")
False
>>> # Contains 4 or more consecutive repeated digits 
>>> CC.is_valid_creditcard("4011-7505-1047-1848")
True
>>> CC.is_valid_creditcard("6015399610667820")
True


如果您不熟悉正则表达式,则可能需要查看此页面。 />

评论


\ $ \ begingroup \ $
您的函数同时失败了连字符(由于进行了任何检查)以及“ 4个连续的重复数字”要求。另外,长度检查是错误的。根据OP,序列可能包含连字符,因此“ 4567-1234-1234-1234”是有效序列,但肯定没有长度16。您可能应该查看(嘿)您的代码和OP的要求。
\ $ \ endgroup \ $
– Zeta
17年7月18日在8:09



\ $ \ begingroup \ $
您的新正则表达式缺少“无连续4位数字”部分。
\ $ \ endgroup \ $
– Zeta
17年7月18日在8:27



\ $ \ begingroup \ $
还是错。现在,任何包含四倍相同数字的信用卡都是错误的,例如4567-1234-4564-4534具有6个4s,但根据规格有效。
\ $ \ endgroup \ $
– Zeta
17年7月18日在8:42



\ $ \ begingroup \ $
我注意到我自己-我现在真的需要变得更有创造力。
\ $ \ endgroup \ $
–丹尼尔(Daniel)
17年7月18日在8:44