As três regex que você precisa
| Uso | Expressão |
|---|---|
| Sem máscara (14 caracteres) | ^[0-9A-Z]{12}\d{2}$ |
| Com máscara (18 caracteres) | ^[0-9A-Z]{2}\.[0-9A-Z]{3}\.[0-9A-Z]{3}\/[0-9A-Z]{4}-\d{2}$ |
| Aceitando as duas formas | ^[0-9A-Z]{2}\.?[0-9A-Z]{3}\.?[0-9A-Z]{3}\/?[0-9A-Z]{4}-?\d{2}$ |
12.ABC.345/01DE-00 casa perfeitamente com todos os padrões acima e mesmo assim é inválido. Sempre combine a regex com o cálculo do módulo 11.O que muda em relação à regex antiga
| Antes (só números) | Agora (alfanumérico) | Observação |
|---|---|---|
^\d{14}$ | ^[0-9A-Z]{12}\d{2}$ | as 12 primeiras posições ganham letras |
^\d{2}\.\d{3}\.\d{3}\/\d{4}-\d{2}$ | ^[0-9A-Z]{2}\.[0-9A-Z]{3}\.[0-9A-Z]{3}\/[0-9A-Z]{4}-\d{2}$ | a máscara não mudou, só o conjunto de caracteres |
[0-9]{14} em busca livre | \b[0-9A-Z]{12}[0-9]{2}\b | para extrair CNPJ de texto corrido |
Por linguagem
const CNPJ_LIMPO = /^[0-9A-Z]{12}\d{2}$/;
const CNPJ_MASCARADO = /^[0-9A-Z]{2}\.[0-9A-Z]{3}\.[0-9A-Z]{3}\/[0-9A-Z]{4}-\d{2}$/;
const CNPJ_QUALQUER = /^[0-9A-Z]{2}\.?[0-9A-Z]{3}\.?[0-9A-Z]{3}\/?[0-9A-Z]{4}-?\d{2}$/;
// Extrair CNPJs de um texto qualquer
const ENCONTRAR = /\b[0-9A-Z]{2}\.?[0-9A-Z]{3}\.?[0-9A-Z]{3}\/?[0-9A-Z]{4}-?\d{2}\b/g;
const texto = 'Contrato firmado com 12.ABC.345/01DE-35 e 11.222.333/0001-81.';
console.log(texto.toUpperCase().match(ENCONTRAR));
import re
CNPJ_LIMPO = re.compile(r"^[0-9A-Z]{12}\d{2}$")
CNPJ_MASCARADO = re.compile(r"^[0-9A-Z]{2}\.[0-9A-Z]{3}\.[0-9A-Z]{3}/[0-9A-Z]{4}-\d{2}$")
# Em Python, prefira fullmatch a match: match casa apenas o começo da string.
bool(CNPJ_LIMPO.fullmatch("12ABC34501DE35")) # True
$limpo = '/^[0-9A-Z]{12}\d{2}$/';
$mascarado = '#^[0-9A-Z]{2}\.[0-9A-Z]{3}\.[0-9A-Z]{3}/[0-9A-Z]{4}-\d{2}$#';
// Use o delimitador # quando o padrão tiver barra, para não precisar escapar.
if (preg_match($limpo, strtoupper($valor))) {
// formato ok — agora valide o dígito verificador
}
-- PostgreSQL (operador ~)
SELECT * FROM empresa WHERE cnpj !~ '^[0-9A-Z]{12}[0-9]{2}$';
-- MySQL 8+
SELECT * FROM empresa WHERE cnpj NOT REGEXP '^[0-9A-Z]{12}[0-9]{2}$';
-- Oracle
SELECT * FROM empresa WHERE NOT REGEXP_LIKE(cnpj, '^[0-9A-Z]{12}[0-9]{2}$');
-- SQL Server não tem regex nativo; use LIKE com classes
SELECT * FROM empresa
WHERE LEN(cnpj) <> 14
OR cnpj LIKE '%[^0-9A-Z]%'
OR SUBSTRING(cnpj, 13, 2) LIKE '%[^0-9]%';
<!-- O pattern do HTML já é ancorado: não use ^ nem $ -->
<input
type="text"
name="cnpj"
inputmode="text"
autocapitalize="characters"
maxlength="18"
placeholder="00.000.000/0000-00"
pattern="[0-9A-Za-z]{2}\.?[0-9A-Za-z]{3}\.?[0-9A-Za-z]{3}/?[0-9A-Za-z]{4}-?[0-9]{2}"
title="Informe um CNPJ com 14 caracteres, por exemplo 12.ABC.345/01DE-35"
required>
Armadilhas comuns
- Usar
^e$em Ruby. Ali eles marcam início e fim de linha; o correto é\Ae\z, senão"lixo\n12ABC34501DE35"passa. - Esquecer o
fullmatchem Python.re.matchcasa só o começo —"12ABC34501DE3500000"seria aceito. - Aceitar acentos.
[A-Z]em modo Unicode não pegaÇ, e está certo: o alfabeto do CNPJ é só ASCII sem acento. - Validar o valor com máscara no banco. Guarde limpo e aplique a regex do formato limpo; a versão mascarada serve para a camada de entrada.
- Backtracking. Evite construções como
([0-9A-Z]+)+ao montar padrões maiores em cima do CNPJ; as expressões desta página são todas lineares.