Validação do número de registro de CNH
06/03/2008
0
Já pesquisei em tudo que é forum e não encontrei uma solução definitiva para o cálculo dos Dígitos Verificadores do Registro de CNH.
O número tem 11 dígitos e os 2 últimos são os DV.
Peguei algumas idéias mas não atendem 100¬ dos números, principalmente os que terminam em ´00´.
Se alguem puder dar uma ajuda, ficarei grato.
Cps.art
Post mais votado
27/03/2013
FUNCTION VRegCnh(PVRegCnh : string) : string; stdcall;
var
j, Mult, Soma, Digito1, Digito2, Incr_dig2: integer;
CNH_Forn, Dig_Forn, Dig_Enc : string;
begin
Result := F;
if length(Trim(PVRegCnh)) < 11 then
Exit;
CNH_Forn := Copy(PVRegCnh,1,9);
Dig_Forn := Copy(PVRegCnh,10,2);
Incr_Dig2 := 0;
Soma := 0;
Mult := 9;
for j := 1 to 9 do
begin
Soma := Soma + (StrToInt(CNH_Forn[j]) * Mult);
Mult := Mult - 1;
end;
Digito1 := Soma Mod 11;
if Digito1 = 10 then
begin
Incr_Dig2 := - 2;
end;
if Digito1 > 9 then
begin
Digito1 := 0;
end;
Soma := 0;
Mult := 1;
for j := 1 to 9 do
begin
Soma := Soma + (StrToInt(CNH_Forn[j]) * Mult);
Mult := Mult + 1;
end;
if (Soma Mod 11) + Incr_Dig2 < 0 then
begin
Digito2 := 11 + (Soma Mod 11) + Incr_Dig2;
end;
if (Soma Mod 11) + Incr_Dig2 >= 0 then
begin
Digito2 := (Soma Mod 11) + Incr_Dig2;
end;
if Digito2 > 9 then
begin
Digito2 := 0;
end;
Dig_Enc := IntToStr(Digito1)+IntToStr(Digito2);
if Dig_Forn = Dig_enc then
begin
Result := V;
end;
if Dig_Forn Dig_enc then
begin
Result := F;
end;
end;
Clovis Perazza
Mais Posts
07/07/2009
Emerson Nascimento
if Digito > 9 then Digito := 11 - Digito;
a fórmula correta vai depender dos números a serem calculados.
07/07/2009
Cps.art
Trabalho no Detran aqui na minha cidade exclusivamente com CNH, então, quando da chegada de CNHs novas, 1ª habilitação, e como são feitas em lotes por cada Ciretran, elas vem numa sequência numérica que eu procurei observar, então dá pra perceber que a sequência está nos 9 primeiros dígitos da esquerda para a direita, como é obvio, então os 2 últimos dígitos são os DVs.
Exemplo da sequência que observei:
043973228XY
043973229XY
043973230XY
Tambem ouvi algum cometário que o primeiro dígito, o X da sequência acima, é calculado de forma completamente diferente do Y, algoritimo diferente.
Agora complicou mais ainda.
Pra quem quiser continuar tentando, aí vai uma série de 23 números de CNH para serem testados.
[b:c2e73664fb]04397322870, 04375701302, 02996843266, 04375700501, 02605113410, 03247061306, 01258750259, 00739751580, 03375637504, 02542551342, 01708111400, 00836510948, 04365445978, 04324384302, 04339482949, 01036520050, 01612581027, 00603454740, 04129251992, 03401740201, 03417248301, 00670431345, 03292694405.[/b:c2e73664fb]
Se encontrarem uma rotina que validem todos eles, certamente essa será a rotina correta.
Fico no aguardo de novas descobertas.
Abraços
08/07/2009
Cps.art
Testei diversos algoritimos numa planilha excel e acho que descobri o segredo do allgoritimo para o segundo DV.
Para o cálculo do 1º DV, o código do [b:67e98cf075]emerson.en[/b:67e98cf075] tá correto, para o segundo é meio diferente.
Pelo menos os 23 números de CNH que coloquei no post anterior validou 100¬.
Amanhã vou testar mais alguns números para ter certeza, depois vou montar a função e passo pra vocês.
Abraços.
08/07/2009
Cps.art
/////////////////////////
[b:04b1d5a323]function VRegCNH(nCNH: string): boolean;
var
j, Soma, Digito1, Digito2, Incr_dig2: integer;
sCNH, Dig_Forn, Dig_Enc : string;
begin
Result := False;
if length(Trim(nCNH)) < 11 then
Exit;
sCNH := Copy(nCNH,1,9);
Dig_Forn := Copy(nCNH,10,2);
Incr_Dig2 := 0;
[color=blue:04b1d5a323]// calcula o digito 1 (10º caractere)[/color:04b1d5a323]
Soma := 0;
for j := 9 downto 1 do
Soma := Soma + ( StrToInt(sCNH[j]) * (10-j));
Digito1 := Soma Mod 11;
if Digito1 >= 10 then
begin
Incr_Dig2 := -2; // [color=red:04b1d5a323]Aqui está o segredo[/color:04b1d5a323]
Digito1 := 0;
end;
[color=blue:04b1d5a323]// calcula o digito 2 (11º caractere)[/color:04b1d5a323]
Soma := 0;
for j := 9 downto 1 do
Soma := Soma + ( StrToInt(sCNH[j]) * (j)); // [color=red:04b1d5a323]Observem que aqui os multiplicadores estão invertidos[/color:04b1d5a323]
Digito2 := Soma Mod 11;
if Digito2 >= 10 then
begin
Digito2 := 0;
end;
Digito2 := Digito2 + Incr_Dig2;
Dig_Enc := IntToStr(Digito1)+IntToStr(Digito2);
Result := Dig_Forn = Dig_enc;
end;
end.[/b:04b1d5a323]
/////////////////////////////////
Testem, depois façam seus comentários.
Abraços.
24/07/2009
Provisorio
Digo ´quase´, pois num laço de repetição que testei uma massa de 3123 números de CNH, apenas 90 não validaram por esta função. Porém estas 90 são CNHs válidas, como pode ser verificado em sites do DETRAN.
Exemplos de CNHs válidas (que não validaram com essa função) são:
112274500 - 113473508 - 115478700 - 134934008 - 138273900 - 138876109 - 186499700 - 218659309 - 219077908 - 241595908 - 293766109 - 293821800 - 301823609 - 307412830 - 308041097 - 308510968 - 308601149 - 308656679 - 309493455 - 309508711 - 309626331 - 310376408 - 313736375 - 314019286 - 314035249 - 315040424 - 315097256 - 315259515 - 315323140 - 315558600 - 316629073 - 317775758 - 318109778 - 318490102 - 318494361 - 327244208 - 330157409 - 374723809 - 413981908 - 416345009 - 530678608 - 679221808 - 697159700 - 709245709 - 715334009 - 804310709 - 869586408 - 894476709
Para conseguir a lista com as 3123 CNHs, basta acessar
http://www.detran.rj.gov.br/_monta_aplicacoes.asp?doc=5646&cod=14&tipo=exibe_noticias&pag_noticias=true
E para testar o dígito verificador oficialmente pelo Detran, podemos usar o link https://wwws.detrannet.mg.gov.br/detran/pontuacond.asp?IdServico=36
Verifiquem. Estou tentando achar qual é o erro. De qualquer maneira, o algoritmo é muito bom, e valida na grande maioria dos casos (apenas 3¬ de erro), porém meio erro ainda é um erro.
Vamos tentar achar um algoritmo que valide 100¬ dos números que passei?
Abraços a todos!
24/07/2009
Emerson Nascimento
24/07/2009
Provisorio
A maioria que não validou teve 9 dígitos.
Faça o teste...
Abraços!
24/07/2009
Cps.art
Essa função que postei aqui é para validar CNHs novas, de 11 dígitos, ou seja, 9 dígitos + 2 verificadores. Ex: 123.456.789-01.
Para validar as CNHs antigas, as chamadas PGU, de 8 dígitos mais 1 DV, o algoritimo é diferente.
Fiz uma correção na função que postei anteriormente, então fica assim:
[b:8147beb8c0]FUNCTION VRegCnh(PVRegCnh : string) : string; stdcall;
var
j, Soma, Digito1, Digito2, Incr_dig2: integer;
CNH_Forn, Dig_Forn, Dig_Enc : string;
begin
Result := ´F´;
if length(Trim(PVRegCnh)) < 11 then
Exit;
CNH_Forn := Copy(PVRegCnh,1,9);
Dig_Forn := Copy(PVRegCnh,10,2);
Incr_Dig2 := 0;
// calcula o digito 1 (10º caractere)
Soma := 0;
for j := 9 downto 1 do
Soma := Soma + ( StrToInt(CNH_Forn[j]) * (10-j));
Digito1 := Soma Mod 11;
if Digito1 >= 10 then
begin
Incr_Dig2 := -2; // Aqui está o segredo
Digito1 := 0;
end;
// calcula o digito 2 (11º caractere)
Soma := 0;
for j := 9 downto 1 do
Soma := Soma + ( StrToInt(CNH_Forn[j]) * (j));
Digito2 := (Soma Mod 11) + Incr_Dig2; // [color=red:8147beb8c0]soma o incremento aqui[/color:8147beb8c0]
if Digito2 >= 10 then
begin
Digito2 := 0;
end;
// Digito2 := Digito2 + Incr_Dig2; // [color=red:8147beb8c0]tira esta linha[/color:8147beb8c0]
Dig_Enc := IntToStr(Digito1)+IntToStr(Digito2);
if Dig_Forn = Dig_enc then
begin
Result := ´V´;
end;
if Dig_Forn <> Dig_enc then
begin
Result := ´F´;
end;
end;[/b:8147beb8c0]
Essa alteração é por que o segundo dígito poderia ser tambem maior que 9, o que faria ele ser ´0´ (zero), e se o primeiro tambem tivesse sido maior que 9, o incremento seria -2, então o segundo seria ´0-2=-2´ o que seria incorreto.
Vou passar a função para validar CNHs antigas, na próxima mensagem.
24/07/2009
Cps.art
É um algoritimo comum, igual ao que valida conta de banco e a maioria dos numeros com DV.
Existem 2 maneiras de se tratar o dígito verificador neste módulo.
1º) Algumas empresas não utiliza ´X´ como dígito, então se o resultado da operação for igual a ´10´, o dígito será ´0´. É o caso das CNHs antigas.
2º) Outras empresas, bancos principalmente, utiliza o ´X´ como dígito, então se o resultado da operação for ´10´ o dígito será ´X´.
Eu não utilizo esta função de validar CNHs antigas por que no meu trabalho não tenho essa necessidade, só utilizo validação de CNHs novas.
Se você tiver a necessidade de utilizar as 2 funções, pode juntar as 2 numa só, definindo no início da função se é CNH nova ou se é CNH antiga pela quantidade de caracteres, ai direciona para o cálculo pretendido.
No meu modo de entender, todos os caracteres do numero devem ser digitados, ou seja os zeros no início, para que a função possa distinguir cada tipo de CNH.
Se existirem 11 caracteres é CNH nova, se existirem 9 caracteres é CNH antiga.
Ai abaixo vai a função para as CNHs antigas.
[b:b180e1b57e]FUNCTION VMod110(PVMod110 : string) : string; stdcall;
var
LenPVMod110 : integer;
MultVMod110 : integer;
SomaVMod110 : integer;
PVMod110New : string;
PVMod110Dig : string;
kvm : integer;
DigMod110 : string;
begin
MultVMod110 := 9;
SomaVMod110 := 0;
PVMod110 := trim(PVMod110);
LenPVMod110 := length(PVMod110);
PVMod110New := Copy(PVMod110,1,LenPVMod110 - 1);
PVMod110Dig := Copy(PVMod110,LenPVMod110,1);
if LenPVMod110 < 2 then
begin
Result := ´F´;
Exit;
end;
if StrToInt(PVMod110New) < 1 then
begin
Result := ´F´;
Exit;
end;
for kvm := LenPVMod110 - 1 downto 1 do
begin
SomaVMod110 := SomaVMod110 + (MultVMod110 * StrToInt(Copy(PVMod110New,kvm,1)));
MultVMod110 := MultVMod110 - 1;
if MultVMod110 = 1 then
begin
MultVMod110 := 9
end;
end;
DigMod110 := IntToStr(SomaVMod110 mod 11);
if DigMod110 = ´10´ then
begin
DigMod110 := ´0´
end;
PVMod110New := PVMod110New + DigMod110;
if PVMod110 = PVMod110New then
Result := ´V´
else begin
Result := ´F´
end;
end;[/b:b180e1b57e]
Teste e depois nos diga se funcionou.
Abraços.
24/07/2009
Provisorio
Testei as novas funções com os 3123 números. Testei a que valida as novas e as antigas, porém algumas (71 cnhs) que validam pelo site do Detran ainda não validam com estas funções.
Alguns exemplos são:
112274500
115478700
138273900
138876109
186499700
218659309
293766109
293821800
301823609
315558600
330157409
374723809
416345009
697159700
709245709
715334009
804310709
894476709
e
01137369409
01217567509
01417566409
01422691909
Estamos quase conseguindo, pois 71 em um universo de 3123 é um número bastante pequeno, porém...
De qualquer forma, agradeço imensamente pelas funções e explicação adicional!
Um abração!
28/07/2009
Provisorio
Eu não consegui passar deste percentual ainda...
01/08/2009
Cps.art
Fiqui uns dias sem micro, meu monitor pifou, estou usando um emprestado até chegar um novo.
Logo dou notícias.
Abraços
07/08/2009
Cps.art
Mudei algumas coisas na função, veja aí se valida todas agora.
Só as CNHs novas, com número de registro de 11 dígitos.
FUNCTION VRegCnh(PVRegCnh : string) : string; stdcall;
var
j, Mult, Soma, Digito1, Digito2, Incr_dig2: integer;
CNH_Forn, Dig_Forn, Dig_Enc : string;
begin
Result := ´F´;
if length(Trim(PVRegCnh)) < 11 then
Exit;
CNH_Forn := Copy(PVRegCnh,1,9);
Dig_Forn := Copy(PVRegCnh,10,2);
Incr_Dig2 := 0;
Soma := 0;
Mult := 9;
for j := 1 to 9 do
begin
Soma := Soma + (StrToInt(CNH_Forn[j]) * Mult);
Mult := Mult - 1;
end;
Digito1 := Soma Mod 11;
if Digito1 = 10 then
begin
Incr_Dig2 := -2;
end;
if Digito1 > 9 then
begin
Digito1 := 0;
end;
Soma := 0;
Mult := 1;
for j := 1 to 9 do
begin
Soma := Soma + (StrToInt(CNH_Forn[j]) * Mult);
Mult := Mult + 1;
end;
if (Soma Mod 11) + Incr_Dig2 < 0 then
begin
Digito2 := 11 + (Soma Mod 11) + Incr_Dig2;
end;
if (Soma Mod 11) + Incr_Dig2 >= 0 then
begin
Digito2 := (Soma Mod 11) + Incr_Dig2;
end;
if Digito2 > 9 then
begin
Digito2 := 0;
end;
Dig_Enc := IntToStr(Digito1)+IntToStr(Digito2);
if Dig_Forn = Dig_enc then
begin
Result := ´V´;
end;
if Dig_Forn <> Dig_enc then
begin
Result := ´F´;
end;
end;
Abraços a todos.
07/08/2009
Cps.art
É que os números foram tomados como ´inteiros´ e os zeros foram suprimidos, por isso a importancia de se tratar os números de CNH considerando os zeros.
Essas abaixo são números de CNHs novas, basta acrescentar os 2 zeros a esquerda:
112274500
115478700
138273900
138876109
186499700
218659309
293766109
293821800
301823609
315558600
330157409
374723809
416345009
697159700
709245709
715334009
804310709
894476709
Para validar os números de CNHs antigas, os PGUs, que são 9 dígitos, sendo os 8 primeiros a numeração sequencial e o último a direita (nono), o DV, vou postar logo abaixo a função.
Abraços
07/08/2009
Cps.art
[b:12b1b92828]FUNCTION VPguCnh(PVPguCnh : string) : string; stdcall;
var
j, Mult, Soma, Digito : integer;
PGU_Forn, Dig_Forn, Dig_Enc : string;
begin
Result := ´F´;
if length(Trim(PVPguCnh)) <> 9 then
Exit;
PGU_Forn := Copy(PVPguCnh,1,8);
Dig_Forn := Copy(PVPguCnh,9,1);
Soma := 0;
Mult := 2;
for j := 1 to 8 do
begin
Soma := Soma + (StrToInt(PGU_Forn[j]) * Mult);
Mult := Mult + 1;
end;
Digito := Soma Mod 11;
if Digito > 9 then
begin
Digito := 0;
end;
Dig_Enc := IntToStr(Digito);
if Dig_Forn = Dig_enc then
begin
Result := ´V´;
end;
if Dig_Forn <> Dig_enc then
begin
Result := ´F´;
end;
end;[/b:12b1b92828]
Aguardo retorno de vocês.
Abraços
Clique aqui para fazer login e interagir na Comunidade :)