Pular para o conteúdo
Documentos

Regex de CNPJ (numérico e alfanumérico) e por que a regex não basta

Publicado em · Atualizado em · 5 min de leitura · Equipe Gerador Brasil

Para CNPJ sem máscara, aceitando os dois formatos, use ^[0-9A-Z]{12}[0-9]{2}$: 12 posições com letras maiúsculas ou dígitos e 2 dígitos verificadores numéricos. Com máscara, ^[0-9A-Z]{2}\.[0-9A-Z]{3}\.[0-9A-Z]{3}/[0-9A-Z]{4}-[0-9]{2}$. A regex só confere o formato: ela aceita qualquer dígito verificador errado e sequências repetidas, então o cálculo do módulo 11 continua sendo obrigatório.

A regex de CNPJ cabe numa linha, e é por isso que ela engana. Abaixo estão as expressões para o formato numérico e para o alfanumérico, testadas com números gerados pela biblioteca do site, e a lista do que elas não pegam. Para validar de fato, use o validador de CNPJ, que confere o formato e o dígito.

As expressões

Para quê Regex
Só numérico, sem máscara ^\d{14}$
Numérico e alfanumérico, sem máscara ^[0-9A-Z]{12}[0-9]{2}$
Numérico e alfanumérico, com máscara ^[0-9A-Z]{2}\.[0-9A-Z]{3}\.[0-9A-Z]{3}/[0-9A-Z]{4}-[0-9]{2}$
Com ou sem máscara (completa) ^(?:[0-9A-Z]{12}[0-9]{2}|[0-9A-Z]{2}\.[0-9A-Z]{3}\.[0-9A-Z]{3}/[0-9A-Z]{4}-[0-9]{2})$

A barra vertical na última linha está escapada só por causa da tabela; na expressão, é a alternância normal.

O desenho vem do formato oficial. Segundo a Receita Federal, as 12 primeiras posições (raiz e ordem) podem ter números de 0 a 9 e letras maiúsculas de A a Z, e o dígito verificador (as 2 últimas) é numérico. A máscara é a mesma do CNPJ de sempre. Como a regra é a mesma para os dois formatos, [0-9A-Z]{12}[0-9]{2} já cobre o numérico.

O que testamos

Rodamos as expressões em JavaScript contra números gerados pela biblioteca do site:

Teste Resultado
^\d{14}$ em 1.000 CNPJs alfanuméricos aceitou 0
^[0-9A-Z]{12}[0-9]{2}$ em 1.000 alfanuméricos aceitou 1.000
a mesma regex em 1.000 numéricos aceitou 1.000
regex com máscara em 1.000 alfanuméricos mascarados aceitou 1.000

Ou seja: a regex antiga, a que todo cadastro tem, rejeita todo CNPJ com letra. Se o seu formulário usa \d{14} ou \d{2}\.\d{3}\.\d{3}/\d{4}-\d{2}, ele recusa o novo formato antes de qualquer outra regra. O passo a passo no banco e na API está em como migrar o banco de dados.

Por que a regex não basta

Os mesmos testes mostram o outro lado:

  • Dígito errado passa. Alteramos o último dígito de 1.000 CNPJs válidos. A regex aceitou os 1.000. O validador recusou os 1.000.
  • Quase tudo que tem 14 dígitos passa. Em 100.000 sequências de 14 dígitos sorteadas, a regex aceitou as 100.000. O cálculo do dígito aceitou 996 (semente 2026), cerca de 1 em 100, que é a chance de dois dígitos ao acaso baterem.
  • Sequência repetida passa. 00000000000000 e 11111111111111 têm o formato certo. Nenhum validador deve aceitá-los.

A conta do dígito é módulo 11, com cada caractere convertido pelo código ASCII menos 48. Regex não faz conta. O formato é a primeira barreira, o dígito é a segunda.

Armadilhas por linguagem

JavaScript. \d só casa os dígitos de 0 a 9. Testamos: /\d/.test('٣') dá false. E, sem a flag m, $ só casa no fim do texto: ^[0-9A-Z]{12}[0-9]{2}$ recusa 12ABC34501DE35\n.

Python. Em padrão de texto (str), \d casa dígitos de outros alfabetos além de 0 a 9, e $ casa também antes de uma quebra de linha no fim. A documentação do módulo re descreve os dois comportamentos. Use [0-9] e re.fullmatch:

import re

CNPJ = re.compile(r"[0-9A-Z]{12}[0-9]{2}")
CNPJ.fullmatch("12ABC34501DE35")    # casa
CNPJ.fullmatch("12ABC34501DE35\n")  # None

Esse trecho em Python não foi executado em nossa máquina de testes; ele segue a documentação do re. Em outras linguagens, confira como $ trata a quebra de linha final antes de confiar na expressão.

Máscaras pela metade

É tentador fazer cada separador opcional (\.?, /?, -?). O custo é aceitar 12.ABC345/01DE-35 e 12ABC.345/01DE35, que ninguém digita de propósito e que mostram um campo mal formatado. Nos testes, a regex com alternância recusou essas duas formas e aceitou a máscara completa e a forma limpa. A alternativa mais simples é tirar a máscara antes e aplicar só a regex sem máscara.

Onde a regex ajuda

A regex tem lugar certo: o campo de tela e a entrada da API. Ela recusa de imediato o que está claramente fora do formato (13 caracteres, símbolo, letra no dígito verificador) e dá uma mensagem útil antes de qualquer cálculo. Depois dela vem o dígito, e só então a consulta ao banco. Nessa ordem, você economiza processamento e mostra ao usuário o erro certo.

Regex mais DV, em código

A combinação que funciona: normalizar, conferir o formato e recusar repetidos com a regex, depois recalcular o dígito.

const SEM_REPETIDO = /^(?!([0-9A-Z])\1{13}$)[0-9A-Z]{12}[0-9]{2}$/;

function dvCnpj(base) {
  const calc = (v) => {
    let soma = 0, peso = 2;
    for (let i = v.length - 1; i >= 0; i--) {
      soma += v[i] * peso;
      peso = peso === 9 ? 2 : peso + 1;
    }
    const resto = soma % 11;
    return resto < 2 ? 0 : 11 - resto;
  };
  const v = [...base].map((c) => c.charCodeAt(0) - 48); // ASCII - 48
  const d1 = calc(v);
  return `${d1}${calc([...v, d1])}`;
}

function cnpjValido(entrada) {
  const s = String(entrada).replace(/[.\/\-\s]/g, '').toUpperCase();
  return SEM_REPETIDO.test(s) && dvCnpj(s.slice(0, 12)) === s.slice(12);
}

cnpjValido('12.ABC.345/01DE-35'); // true  (exemplo oficial da Receita)
cnpjValido('12abc34501de35');     // true  (maiúsculas após normalizar)
cnpjValido('12ABC34501DE00');     // false (formato certo, dígito errado)

A parte (?!([0-9A-Z])\1{13}$) é uma checagem negativa: recusa o texto se os 14 caracteres forem o mesmo. Executamos o código acima, e as três chamadas devolveram os valores dos comentários.

O que nenhuma validação diz

Passar na regex e no dígito mostra que o número está bem formado. Não mostra que a empresa existe. Isso só uma consulta ao cadastro da Receita confirma, e em teste de software ela não é necessária.

Outro cuidado: a Receita informa que vai bloquear internamente algumas combinações de letras, para evitar palavras ofensivas ou confusão com números, e orienta que os sistemas aceitem todas as letras do alfabeto. Não tente reproduzir essa lista na regex.

Para testar a sua regex com números prontos, gere uma lista com os dois formatos no gerador de CNPJ, com casos inválidos incluídos. Para conferir um número e ver o cálculo do dígito passo a passo, use o validador de CNPJ. Os números gerados são fictícios e valem apenas para teste de software.

Números fictícios, apenas para teste de software. Eles são sorteados no seu navegador e seguem a regra do dígito verificador, mas não vêm de nenhum cadastro. Usar documento falso para enganar alguém é crime.
Dúvidas

Perguntas frequentes

Qual a regex do CNPJ alfanumérico?

Sem máscara: ^[0-9A-Z]{12}[0-9]{2}$. As 12 primeiras posições aceitam letras de A a Z e números, e as 2 últimas só números, porque são os dígitos verificadores.

A regex antiga de CNPJ, com \d, ainda funciona?

Só para os CNPJs numéricos. Em 1.000 CNPJs alfanuméricos gerados para teste, a expressão ^\d{14}$ aceitou zero. Troque \d por [0-9A-Z] nas 12 primeiras posições.

A regex valida o dígito verificador do CNPJ?

Não. Em 1.000 CNPJs com o dígito alterado de propósito, a regex aceitou os 1.000. O dígito exige o cálculo do módulo 11, que uma expressão regular não faz.

Como aceitar CNPJ com e sem máscara na mesma regex?

Use uma alternância entre a forma sem máscara e a forma completa com máscara. Evite pontos e barra opcionais um a um, porque eles aceitam máscaras pela metade como 12.ABC345/01DE-35.

Devo aceitar letra minúscula?

O número oficial usa só maiúsculas. Converta a entrada para maiúsculas antes de aplicar a regex, em vez de liberar minúsculas na expressão, para gravar sempre no mesmo formato.

Fontes

Blog

Leia também

Ferramenta

Veja também

Atualizado em 14/11/2026.