Validação do número de registro de CNH

06/03/2008

0

Olá pessoal.

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

Cps.art

Responder

Post mais votado

27/03/2013

A função de validação de números de registro de CNHs novas (com foto) vai abaixo:



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

Clovis Perazza
Responder

Mais Posts

07/07/2009

Emerson Nascimento

ou talvez para:
    if Digito > 9 then
      Digito := 11 - Digito;

a fórmula correta vai depender dos números a serem calculados.


Responder

07/07/2009

Cps.art

Pelo que andei pesquisando, acho que o número de registro de CNH tem na verdade 2 DV, como o CPF, só que com algoritimo diferente.
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


Responder

08/07/2009

Cps.art

Viva pessoal, descobri !!!!!!!!!

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.


Responder

08/07/2009

Cps.art

Pois bem pessoal, como prometi ai vai a função.

/////////////////////////
[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.


Responder

24/07/2009

Provisorio

A função apresentada está quase correta!

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!


Responder

24/07/2009

Emerson Nascimento

essa rotina foi desenvolvida para validar as CNHs novas, de 11 dígitos.


Responder

24/07/2009

Provisorio

Não, na verdade esta rotina validou todas as que tinham 7 e 8 dígitos! E não foram poucas! O esquema que deve ser feito é antes de começar os cálculos, acrescentar com zeros à esquerda até completar os 11 algarismos.

A maioria que não validou teve 9 dígitos.

Faça o teste...

Abraços!


Responder

24/07/2009

Cps.art

[b:8147beb8c0]Provisório[/b:8147beb8c0], o que o [b:8147beb8c0]emerson.en[/b:8147beb8c0] disse está correto.
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.


Responder

24/07/2009

Cps.art

Esta função valida as CNHs antigas.

É 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.


Responder

24/07/2009

Provisorio

Obrigado pela atenção e interesse!

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!


Responder

28/07/2009

Provisorio

E então, conseguiu mais alguma maneira?
Eu não consegui passar deste percentual ainda...


Responder

01/08/2009

Cps.art

Olá pssoal.
Fiqui uns dias sem micro, meu monitor pifou, estou usando um emprestado até chegar um novo.

Logo dou notícias.

Abraços


Responder

07/08/2009

Cps.art

Vamos tentar mais uma vez, [b:63f43ee594]Provisorio[/b:63f43ee594].

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.


Responder

07/08/2009

Cps.art

Os números de CNHs abaixo, que o [b:6145ec88c1]Provisorio[/b:6145ec88c1] colocou na mensagem, e que não valida o DV, apesar de conterem somente 9 dígitos, não são números de CNHs antigas, o tal de PGU, basta acrescentar dois zeros a esquerda e checa-las como CNH nova, nessa função que postei acima, que os DV validam.
É 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


Responder

07/08/2009

Cps.art

Ai vai a função para checar números de CNHs antigas, os PGUs.


[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


Responder

Que tal ter acesso a um e-book gratuito que vai te ajudar muito nesse momento decisivo?

Ver ebook

Recomendado pra quem ainda não iniciou o estudos.

Eu quero
Ver ebook

Recomendado para quem está passando por dificuldades nessa etapa inicial

Eu quero

Utilizamos cookies para fornecer uma melhor experiência para nossos usuários, consulte nossa política de privacidade.

Aceitar