Validar estrutura e dígitos verificadores dos dois formatos sem transformar o identificador em número
Tutorial · prática orientada · IntermediárioUnits, strings, funções e leitura de laços
Unit compilada e testada com DCC32 37.0, build 37.0.59082.6021. Referência: Delphi 11.3 e 13.2, não verificados individualmente.Documentação, instalação e limites de CSCoder.Validacoes
Experimentar o contrato no navegador
Um cadastro começa a recusar um cliente porque o CNPJ contém letras. A correção parece simples: liberar o teclado e alterar a máscara. Mas o problema também pode estar no banco, em uma expressão regular, em um DTO ou numa rotina que chama StrToInt64.
Vamos implementar ValidarNovoCNPJ. Ela aceita os CNPJs alfanuméricos e também os numéricos compatíveis. A mesma unit oferece ValidarAntigoCNPJ, que exige apenas números na identificação. Use a primeira em cadastros que precisam conviver com os dois padrões; reserve a segunda para contratos explicitamente numéricos. A unit não depende de formulário, rede ou banco de dados: pode ser utilizada por VCL, FireMonkey, UniGUI e serviços.
O que mudou no CNPJ
O novo formato mantém 14 posições: as primeiras 12 aceitam números e letras maiúsculas de A a Z; as duas últimas continuam numéricas. CNPJs existentes permanecem válidos. A coexistência exige que a aplicação aceite os dois formatos. Perguntas e respostas da Receita Federal.
O assunto já chegou aos cadastros: a Receita informou a emissão do primeiro CNPJ alfanumérico em 31 de julho de 2026, com o número 00.000.000/E08G-12. Ele será um dos vetores de teste deste artigo. Comunicado oficial.
Validade matemática não comprova cadastro
O contrato das funções é pequeno: verificar formato e dígitos verificadores. True não significa que a empresa existe, está ativa ou pertence a quem informou o número. Quando o negócio exigir comprovação cadastral, trate a consulta como uma etapa separada, com seus próprios erros e critérios.
Também não transforme CNPJ em inteiro. O identificador precisa preservar zeros à esquerda e agora pode conter letras. No seu contrato de aplicação, use string; no JSON, uma string; no armazenamento, uma coluna textual adequada. Essa decisão evita que uma conversão incidental modifique a identidade que o usuário informou.
O detalhe que muda a conta
O módulo 11 continua em uso. A diferença é converter cada caractere com seu código ASCII menos 48: 0 vale 0, 9 vale 9, A vale 17 e Z vale 42. Não é uma conversão em base 36, em que A teria outro valor.
Os pesos percorrem a base da direita para a esquerda, de 2 a 9, reiniciando em 2. O primeiro DV usa as 12 posições; o segundo inclui o primeiro DV calculado. Resto 0 ou 1 produz DV 0; nos demais casos, o DV é 11 menos o resto. O exemplo oficial 12.ABC.345/01DE-35 resulta em somas 459 e 424, produzindo os dígitos 3 e 5. Manual técnico da Receita/Serpro.
No Delphi, Ord retorna o valor do caractere. Antes de usá-lo na conta, nossa rotina limita o alfabeto aos caracteres ASCII permitidos. Um caractere Unicode visualmente parecido com A ou 5 não deve entrar silenciosamente no cálculo.
Defina o que a entrada pode conter
Esta implementação aceita o valor sem pontuação, com 14 posições, ou a máscara completa XX.XXX.XXX/XXXX-DD, com 18. A posição de cada separador é conferida. Espaços externos são removidos por Trim; letras minúsculas ASCII são convertidas para maiúsculas como conveniência de digitação. O identificador normalizado segue o alfabeto oficial.
Espaços internos, acentos, símbolos extras, máscara quebrada e letras nos DVs são rejeitados. Isso é uma escolha explícita do nosso contrato de entrada, não uma autorização para modificar identificadores arbitrários. Uma rotina que “remove tudo que não é número” destruiria exatamente as letras que precisamos preservar.
O valor inteiramente zerado merece uma rejeição adicional: a operação matemática produziria 00, mas essa sequência não deve ser aceita como identificação. Já zeros na raiz não são motivo de rejeição — o exemplo real da Receita começa com eles.
Unit CSCoder.Validacoes: duas funções booleanas
Salve como CSCoder.Validacoes.pas e adicione a unit ao projeto. Os helpers ficam na implementação; a interface pública expõe duas funções booleanas.
{
CSCoder.Validacoes
Claudio Santos | CS Coder | https://cscoder.com.br/
SPDX-License-Identifier: MIT
Uso, modificacao e redistribuicao permitidos, inclusive em projetos
comerciais. Preserve o aviso de autoria e o texto da licenca.
Fornecido sem garantias, conforme a licenca abaixo.
MIT License
Copyright (c) 2026 Claudio Santos (CS Coder)
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
}
unit CSCoder.Validacoes;
interface
function ValidarNovoCNPJ(const Valor: string): Boolean;
function ValidarAntigoCNPJ(const Valor: string): Boolean;
implementation
uses System.SysUtils;
function Normalizar(const Valor: string; out CNPJ: string): Boolean;
var
S: string;
I: Integer;
C: Char;
begin
Result := False;
CNPJ := '';
S := Trim(Valor);
case Length(S) of
14: CNPJ := S;
18:
begin
if (S[3] <> '.') or (S[7] <> '.') or
(S[11] <> '/') or (S[16] <> '-') then
Exit;
CNPJ := Copy(S, 1, 2) + Copy(S, 4, 3) +
Copy(S, 8, 3) + Copy(S, 12, 4) + Copy(S, 17, 2);
end;
else
Exit;
end;
for I := 1 to 12 do
begin
C := CNPJ[I];
// Apenas letras ASCII: nao converte caracteres Unicode parecidos.
if (C >= 'a') and (C <= 'z') then
C := Char(Ord(C) - Ord('a') + Ord('A'));
if not (((C >= '0') and (C <= '9')) or
((C >= 'A') and (C <= 'Z'))) then
Exit;
CNPJ[I] := C;
end;
for I := 13 to 14 do
if (CNPJ[I] < '0') or (CNPJ[I] > '9') then
Exit;
// Todos os zeros passam na conta, mas nao identificam um CNPJ.
if CNPJ = '00000000000000' then
Exit;
Result := True;
end;
function CalcularDV(const Base: string): Integer;
var
I, Peso, Soma, Resto: Integer;
begin
Soma := 0;
Peso := 2;
for I := Length(Base) downto 1 do
begin
Soma := Soma + (Ord(Base[I]) - Ord('0')) * Peso;
Inc(Peso);
if Peso > 9 then
Peso := 2;
end;
Resto := Soma mod 11;
if Resto < 2 then
Result := 0
else
Result := 11 - Resto;
end;
function ValidarNovoCNPJ(const Valor: string): Boolean;
var
S, Base: string;
DV1, DV2: Integer;
begin
Result := False;
if not Normalizar(Valor, S) then
Exit;
Base := Copy(S, 1, 12);
DV1 := CalcularDV(Base);
if Ord(S[13]) - Ord('0') <> DV1 then
Exit;
DV2 := CalcularDV(Base + Char(Ord('0') + DV1));
Result := Ord(S[14]) - Ord('0') = DV2;
end;
function ValidarAntigoCNPJ(const Valor: string): Boolean;
var
S: string;
I: Integer;
begin
Result := False;
if not Normalizar(Valor, S) then
Exit;
for I := 1 to 12 do
if (S[I] < '0') or (S[I] > '9') then
Exit;
Result := ValidarNovoCNPJ(S);
end;
end.
O normalizador verifica o tamanho antes de acessar as posições. O cálculo percorre no máximo 13 caracteres, com valores e pesos pequenos; Integer comporta a soma com folga. O segundo DV utiliza o primeiro que foi calculado, e não uma letra ou número arbitrário extraído de uma entrada sem validação.
Como chamar sem amarrar a regra à tela
uses CSCoder.Validacoes;
// A tela decide como apresentar a mensagem.
if not ValidarNovoCNPJ('12.ABC.345/01DE-35') then
raise EArgumentException.Create('Confira o CNPJ informado.');O trecho acima pressupõe System.SysUtils para a exceção. Em um endpoint, você pode devolver um erro de entrada; no mobile, destacar o campo; em uma rotina de importação, registrar a linha recusada. A função não precisa conhecer nenhuma dessas apresentações.
Para persistir, mantenha uma política única de normalização e aplique-a de forma consistente na tela, API e importação. Não salve ora com máscara, ora sem máscara, esperando que um índice de unicidade descubra que os valores representam o mesmo identificador.
Casos que precisam entrar no teste
| Entrada | Resultado esperado | Motivo |
|---|---|---|
12.ABC.345/01DE-35 |
True | Exemplo do manual |
00.000.000/E08G-12 |
True | Exemplo divulgado pela Receita |
12.345.678/0001-95 |
True | Compatibilidade numérica; exemplo matemático |
12abc34501de35 |
True | Política de normalização ASCII |
12ABC34501DE34 |
False | Segundo DV alterado |
12ABC34501DEA5 |
False | Letra no primeiro DV |
12.ABC.345-01DE/35 |
False | Separadores fora de posição |
00000000000000 |
False | Sequência zerada |
O projeto para download contém 370 verificações. Além das entradas de borda, há 80 bases sintéticas, com DVs obtidos por uma referência de pesos explícitos, e 80 versões com o segundo DV alterado. Esses dados exercitam o algoritmo; não representam empresas inscritas. A unit e o runner foram compilados com DCC32 37.0, build 37.0.59082.6021, com verificações de faixa e overflow ativadas. As versões 11.3 e 13.2 não foram executadas individualmente.
A função é só uma parte da adaptação
Procure no projeto por conversões numéricas de CNPJ, validações baseadas exclusivamente em dígitos, máscaras, tipos dos campos persistentes e regras de importação. Confira também se a tela mobile permite digitar letras. Depois percorra um cadastro completo: entrada, validação, envio, armazenamento e leitura de volta.
Faça isso com um exemplo numérico e um alfanumérico, preservando zeros à esquerda. Integrações fiscais e serviços externos devem ser avaliados conforme seus contratos e documentação próprios; passar em ValidarNovoCNPJ não comprova que um arquivo fiscal ou uma requisição será aceito.
Como exercício, escreva testes que recusem pontuação duplicada e dígitos de largura completa, mantendo os exemplos válidos. Em seguida, leia Tela, serviço e domínio para decidir onde colocar essa regra no seu cadastro.
Licença: código livre com crédito preservado
A unit e o projeto de testes são disponibilizados sob a licença MIT, com crédito a Claudio Santos (CS Coder). Você pode utilizar, modificar e redistribuir o código, inclusive em aplicações comerciais e de código fechado, preservando o aviso de autoria e o texto da licença nas cópias ou partes substanciais do código. O cabeçalho do .pas contém a licença completa; o ZIP inclui LICENSE.txt.
O código é fornecido sem garantias, conforme o texto da licença. Essa permissão se aplica ao pacote de código, não é uma licença geral para as imagens, a marca ou os demais artigos. Texto da licença MIT.
Downloads: inclua a unit no seu projeto
O arquivo é a mesma unit exibida neste artigo, pronta para adicionar ao projeto. Inclua CSCoder.Validacoes no uses e chame uma das funções. A compilação e os 370 resultados do runner foram verificados antes de disponibilizar o arquivo. Não há dependências de terceiros; a implementação utiliza System.SysUtils.
O pacote abaixo inclui também o projeto console de testes. Para outros exemplos e recursos, consulte a página de downloads.
Inspecione a conta e o contrato
Manual de DV da Receita conferido em 08/10/2026; vetor técnico 12ABC34501DE35.
- No laboratório CNPJ, confira normalização ASCII.
- Observe valores: código ASCII menos 48; A contribui com 17.
- Compare pesos, soma e resto módulo 11 do primeiro DV. Acrescente-o e repita para o segundo.
- Altere o DV e confirme recusa; compare 11222333000181 e todos os zeros.
Checklist: campo textual, máscaras, integrações, importação/exportação e ordenação; não converter documento em número. Mudanças de banco ficam adiadas. Formato e DV não comprovam existência ou situação cadastral.