
Algoritmos para validação de CPF são alguns dos primeiros algoritmos que aprendemos em cursos de programação (pelo menos foi assim comigo).
CPFs são compostos por 11 dígitos, sendo que os 2 últimos são dígitos verificadores. Portanto, para sabermos se um CPF é ou não válido, precisamos realizar o cálculo (que foi definido pela Receita Federal) e comparar o resultado obtido com os valores do dígito verificador.
O cálculo é bem simples! Vamos imaginar o seguinte CPF:
504.065.090-69 (CPF gerado aleatoriamente)
Primeiro pegamos os 9 primeiros dígitos e aplicamos a cada um deles um peso que se inicia em 2, da direita para a esquerda.
Após isso, basta somarmos cada dígito multiplicado por seu peso:
5 * 10 + 0 * 9 + 4 * 8 + ... + 0 * 2 = 170
Com este resultado, podemos optar entre 2 cálculos:
11 - 170 % 11
ou
170 * 10 % 11
Obs.: Lembrando que "%" é usado para calcular o resto (módulo) da divisão na maioria das linguagens de programação.
Ambos os cálculos nos levará ao valor do primeiro dígito verificador, que para este exemplo é 6.
Agora basta fazermos o mesmo calculo anterior, mas considerando também o primeiro dígito.
Os pesos ficarão conforme abaixo:
Como a partir daqui o cálculo é exatamente o mesmo, vamos então para o algoritmo em Python!
TAMANHO_CPF = 11
def is_cpf_valido(cpf: str) -> bool:
if len(cpf) != TAMANHO_CPF:
return False
if cpf in (c * TAMANHO_CPF for c in "1234567890"):
return False
cpf_reverso = cpf[::-1]
for i in range(2, 0, -1):
cpf_enumerado = enumerate(cpf_reverso[i:], start=2)
dv_calculado = sum(map(lambda x: int(x[1]) * x[0], cpf_enumerado)) * 10 % 11
if cpf_reverso[i - 1:i] != str(dv_calculado % 10):
return False
return True
if __name__ == "__main__":
print(is_cpf_valido("0"))
print(is_cpf_valido("123456789012345"))
print(is_cpf_valido("55555555555"))
print(is_cpf_valido("19627722000"))
print(is_cpf_valido("19627722091"))
print(is_cpf_valido("19627722090"))
print(is_cpf_valido("02927367035"))
print(is_cpf_valido("64935775009"))
print(is_cpf_valido("45508834052"))
print(is_cpf_valido("50406509069"))
Na linha 1 apenas definimos uma constante com o tamanho do CPF.
Na linha 4 temos a declaração da nossa função.
Na linha 5 realizamos nossa primeira validação. Se o CPF não tiver exatamente 11 caracteres retornamos que o CPF informado é inválido. (lembre-se de limpar a mascara do CPF antes de chamar esta função).
Na linha 8 validamos se não é um CPF inválido conhecido (111.111.111-11, 222.222.222-22, ..., 000.000.000-00). Esta validação é feita a partir de um iterável gerado com uma comprehension. A comprehension irá iterar sobre cada valor da string "1234567890" e irá multiplicar cada caractere pelo tamanho do CPF. Aqui temos 2 particularidades sobre strings no Python:
1. Strings são iteráveis
2. Quando multiplicamos uma string por um valor inteiro, o resultado obtido será aquela string repetida x vezes.
Exemplo:
"a" * 10 = 'aaaaaaaaaa'
Na linha 11 o CPF informado é invertido. Exemplo:
De 12345678910 para 01987654321
A inversão é feita para facilitar a aplicação dos pesos.
Na linha 12 nós declaramos um for que irá iterar 2x, iniciando em 2 e finalizando em 1.
Na linha 13 é onde aplicamos os pesos de cada digito do CPF.
Primeiro nós pegamos o CPF invertido e fazemos uma substring delete que vai de "i" até o final.
Então pegamos esta substring e aplicamos os pesos nela, através da função "enumerate".
A função enumerate merece um post só para ela. Mas, de maneira resumida, ela recebe um iterável e enumera cada posição deste iterável. Ela retorna um "enumerate object" (que é também um iterável) de tuplas. Podemos converter este "enumarete object" em uma lista, por exemplo.
Exemplo:
list(enumerate([1, "b", 3])) = [(0, 1), (1, 'b'), (2, 3)]
Obs.: Vale atentar que no nosso caso nós iniciamos a função enumerate em 2 e não em 0.
A linha 14 é onde aplicamos o cálculo explicado no começo do post.
Primeiro multiplicamos os dígitos por seus respectivos pesos através da função "map". Assim como a função enumerate, a função map merece um post só para falar dela, mas vamos para um resumo. Ela recebe uma função e um iterável, e a partir disso gera um novo iterável.
No nosso exemplo, a função informada é uma expressão lambda (que resumidamente é uma função anônima) e o enumerate object criado na linha anterior. É na expressão lambda que ocorre a multiplicação dos digitos pelos seus pesos.
Usamos então função sum para somar o iterável retornado pela função map e aplicamos o resto da fórmula.
Na linha 15 nós comparamos o dígito do CPF informado com o dígito calculado. Caso os valores sejam diferentes, retornamos que o CPF é inválido.
A única observação que cabe aqui (além da conversão do dígito calculado para string) é que nós comparamos o módulo do dígito calculado por 10. Fazemos isso, pois quando o módulo da divisão por 11 resulta em 10, o valor que deve ser considerado é 0.
Como dito anteriormente, o loop será executado 2x. E caso não haja nenhuma inconsistência, a função irá retornar que o CPF é válido (linha 18).