对增值税号校验位算法不满意

时间:2014-01-29 19:58:29

标签: python algorithm python-3.x

我编写了一个功能,用于检查公司人格的增值税号码。这个功能有效,但很难看(太长太复杂)。

我是Python的新编程,想要改进功能,所以我需要一些反馈和帮助。

增值税号码始终为9位数,第一位是char,其余为数字。

char必须位于letras字符串中。

最后一个数字是校验位。

使用其他七个数字算法是这样的:

  1. 偶数位数以小计加总。
  2. 奇数阶的数字乘以2(每一个),如果每个的结果是> 10然后将单位添加到十位,然后将四个结果小计化。
  3. 添加了步骤1和2中的小计。
  4. 获取单位,如果为0,则校验位为0,在其他情况下为 扣除10.结果是校验位。
  5. 有效增值税号的示例是A58818501

    代码

    def validarCodigoCIF(entrada):
        """
        :param: entrada: str
        :rtype: bool
        """
        letras = "ABCDEFGHIJKLMNPQRSVW"
        if len(entrada) != 9 or entrada[0] not in letras:
            return False
        numero = entrada[1:10]
        pares = int(numero[1]) + int(numero[3]) + int(numero[5])
        impares = 0
        for i in range(0, 8, 2):
            j = int(numero[i]) * 2
            if j < 10:
                impares += j
            else:
                impares += j - 9
        digito = str(pares+impares)[-1]
        if int(digito) == 0:
            checkCIF = 0
        else:
            checkCIF = 10 - int(digito)
        # print(checkCIF)
        if str(checkCIF) == entrada[-1]:
            return True
        else:
            return False
    
    entradaCodigoCIF = input('Enter the VAT number: ')
    print(validarCodigoCIF(entradaCodigoCIF))
    

3 个答案:

答案 0 :(得分:1)

这是缩短版

def check(vat_num):
    if len(vat_num) != 9 or vat_num[0] not in "ABCDEFGHIJKLMNPQRSVW":
        return False # trivial checks first
    nums = list(map(int, vat_num[1:8])) # numbers to check
    nums[1::2] = ((i * 2) - (9 if i > 4 else 0) for i in nums[1::2]) # alter odds
    return int(vat_num[8]) == (sum(nums) % 10) # compare to check digit

请注意以下有趣的*功能:

  1. 使用[1::2]从迭代中获取所有其他项目;
  2. 使用map(int, ...)将interable中的所有项目转换为整数;和
  3. 使用% 10(模数)轻松获取数字的数字部分。
  4. *可能没什么兴趣

答案 1 :(得分:0)

要解决的第一个问题是你的功能太长了:

def calculate_pares(numero):
    return int(numero[1]) + int(numero[3]) + int(numero[5])

def calculate_impares(numero):
    result = 0
    for i in range(0, 8, 2):
        j = int(numero[i]) * 2
        if j < 10:
            result += j
        else:
            result += j - 9

    return result

def calculate_check_cif(entrada):
    numero = entrada[1:10]
    pares = calculate_pares(numero)
    impares = calculate_impares(numero)

    digito = str(pares+impares)[-1]
    if int(digito) == 0:
        return 0

    return 10 - int(digito)

def validarCodigoCIF(entrada):
    """
    :param: entrada: str
    :rtype: bool
    """
    letras = "ABCDEFGHIJKLMNPQRSVW"
    if len(entrada) != 9 or entrada[0] not in letras:
        return False

    checkCIF = calculate_check_cif(entrada)

    # print(checkCIF)
    return str(checkCIF) == entrada[-1]

这只是第一步,我想你明白了。当你有小功能时,进一步的改进会更容易。

此外,如果您用英语编写代码会更容易 - 那么人们可能会帮助您发现更有意义的功能,而不仅仅是盲目分区/猜测。

答案 2 :(得分:0)

看到增值税号使用Luhn校验和,我提供了我最近在an (unaccepted) answer中发布的有关信用卡号的修改后的版本。

该函数不一定比您的短,但使用另一种方法:它返回有效数字上的None和失败时的短错误消息。它还确保首字母后的数字有效,并允许空格和一些标点符号。

def invalid_cif_code(s):
    """ Returns None if s is a valid CIF code and a string 
        describing the error otherwise. The string may contain 
        spaces and punctuation, which is ignored.
    """

    # strip punctuation from string
    s = [x for x in s if x not in " \t.-"]

    if len(s) != 9:
        return "Invalid length"

    if s[0] not in "ABCDEFGHIJKLMNPQRSVW":
        return "Invalid initial letter"

    # convert numerical part to list of digits
    try:
        code = [int(x) for x in s[1:]]
    except ValueError:
        return "Invalid non-digit in number"

    # multiply and cast out nines for every other codeit   
    for i in range(0, 8, 2):
        code[i] *= 2
        if code[i] > 9:
            code[i] -= 9

    # calculate checksum            
    s = (9 * sum(code)) % 10

    if s != 0:
        return "Invalid checksum"

    return None

Typical Programmer有一个更优雅的Luhn校验和解决方案(虽然用于信用卡号码并用Javascript编写),它使用数组作为奇数。