Muito sistema fiscal brasileiro roda em Delphi, e é exatamente onde o CNPJ costuma estar guardado em campo numérico. A unit abaixo funciona do Delphi 2009 em diante (e no Lazarus/Free Pascal) e não depende de nada além da SysUtils.
0=0 … 9=9, A=17 … Z=42), os pesos vão de 2 a 9 da direita para a esquerda, soma-se tudo, divide-se por 11 e o dígito é 0 quando o resto é menor que 2, ou 11 menos o resto. Ver a conta detalhada.Código completo
unit uCnpj;
interface
function LimparCnpj(const Valor: string): string;
function CalcularDvCnpj(const Base: string): string;
function CnpjValido(const Valor: string): Boolean;
function FormatarCnpj(const Valor: string): string;
implementation
uses
SysUtils;
function LimparCnpj(const Valor: string): string;
var
I: Integer;
C: Char;
begin
Result := '';
for I := 1 to Length(Valor) do
begin
C := UpCase(Valor[I]);
if CharInSet(C, ['0'..'9', 'A'..'Z']) then
Result := Result + C;
end;
end;
{ Calcula um dígito do módulo 11 (valor do caractere = ASCII - 48). }
function Digito(const Sequencia: string): Integer;
var
I, Soma, Valor, Peso, Resto, Tamanho: Integer;
begin
Soma := 0;
Tamanho := Length(Sequencia);
for I := 1 to Tamanho do
begin
Valor := Ord(Sequencia[I]) - 48;
Peso := ((Tamanho - I) mod 8) + 2;
Soma := Soma + (Valor * Peso);
end;
Resto := Soma mod 11;
if Resto < 2 then
Result := 0
else
Result := 11 - Resto;
end;
function CalcularDvCnpj(const Base: string): string;
var
Primeiro, Segundo: Integer;
begin
Primeiro := Digito(Base);
Segundo := Digito(Base + IntToStr(Primeiro));
Result := IntToStr(Primeiro) + IntToStr(Segundo);
end;
function TodosCaracteresIguais(const Texto: string): Boolean;
var
I: Integer;
begin
Result := True;
for I := 2 to Length(Texto) do
if Texto[I] <> Texto[1] then
begin
Result := False;
Exit;
end;
end;
function CnpjValido(const Valor: string): Boolean;
var
Cnpj: string;
I: Integer;
begin
Cnpj := LimparCnpj(Valor);
if Length(Cnpj) <> 14 then
Exit(False);
{ Os dois dígitos verificadores continuam sendo numéricos }
for I := 13 to 14 do
if not CharInSet(Cnpj[I], ['0'..'9']) then
Exit(False);
if TodosCaracteresIguais(Cnpj) then
Exit(False);
Result := CalcularDvCnpj(Copy(Cnpj, 1, 12)) = Copy(Cnpj, 13, 2);
end;
function FormatarCnpj(const Valor: string): string;
var
Cnpj: string;
begin
Cnpj := LimparCnpj(Valor);
if Length(Cnpj) <> 14 then
Exit(Cnpj);
Result := Copy(Cnpj, 1, 2) + '.' + Copy(Cnpj, 3, 3) + '.' +
Copy(Cnpj, 6, 3) + '/' + Copy(Cnpj, 9, 4) + '-' +
Copy(Cnpj, 13, 2);
end;
end.
Testes e integração com o framework
procedure TFrmEmpresa.edtCnpjExit(Sender: TObject);
begin
if edtCnpj.Text = '' then
Exit;
if not CnpjValido(edtCnpj.Text) then
begin
ShowMessage('CNPJ inválido. Confira os dígitos verificadores.');
edtCnpj.SetFocus;
Exit;
end;
edtCnpj.Text := FormatarCnpj(edtCnpj.Text);
end;
{ Antes de julho de 2026, troque no DataModule:
FieldByName('CNPJ').AsInteger -> FieldByName('CNPJ').AsString
e no banco:
ALTER TABLE EMPRESA ALTER COLUMN CNPJ TYPE VARCHAR(14); }
Detalhes que costumam quebrar em Delphi
- Strings em Object Pascal são indexadas a partir de 1 — por isso o peso usa
(Tamanho - I) mod 8e não(Tamanho - 1 - I). UpCasedo Delphi moderno cobre apenas ASCII, que é exatamente o que precisamos aqui.- O maior risco em legado Delphi não é o cálculo: são as
TIntegerField, máscaras!99.999.999/9999-99;1;_eStrToIntespalhados. Faça um find in files porCnpjantes de migrar. - Máscara para
TMaskEditcom letras:!CC.CCC.CCC/CCCC-99;1;_.
Casos de teste recomendados
Cubra pelo menos estes cenários — eles pegam praticamente todos os erros de implementação:
| Entrada | Esperado | O que testa |
|---|---|---|
12.ABC.345/01DE-35 | válido | exemplo oficial, com máscara |
12abc34501de35 | válido | normalização para maiúsculas |
11.222.333/0001-81 | válido | compatibilidade com o formato numérico |
11.222.333/0001-82 | inválido | dígito verificador errado |
12ABC34501DEAB | inválido | letra no dígito verificador |
12ABC34501DE3 | inválido | tamanho incorreto |
00.000.000/0000-00 | inválido | sequência repetida |
"" / null | inválido | entrada vazia sem exceção |
Precisa de mais massa de teste? O gerador produz até 500 CNPJs alfanuméricos válidos de uma vez, com opção de baixar em .txt.