Pacote Go idiomático, sem dependências externas e sem alocação desnecessária — o cálculo percorre a string uma vez por dígito.
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
package cnpj
import (
"regexp"
"strconv"
"strings"
)
var (
naoAlfanumerico = regexp.MustCompile(`[^0-9A-Z]`)
formato = regexp.MustCompile(`^[0-9A-Z]{12}[0-9]{2}$`)
)
// Limpar remove a máscara e normaliza para maiúsculas.
func Limpar(valor string) string {
return naoAlfanumerico.ReplaceAllString(strings.ToUpper(valor), "")
}
// digito calcula um dígito do módulo 11 (valor do caractere = ASCII - 48).
func digito(sequencia string) int {
soma := 0
tamanho := len(sequencia)
for i := 0; i < tamanho; i++ {
valor := int(sequencia[i]) - 48
peso := ((tamanho-1-i)%8 + 2)
soma += valor * peso
}
resto := soma % 11
if resto < 2 {
return 0
}
return 11 - resto
}
// CalcularDV devolve os dois dígitos verificadores da base de 12 caracteres.
func CalcularDV(base string) string {
primeiro := digito(base)
segundo := digito(base + strconv.Itoa(primeiro))
return strconv.Itoa(primeiro) + strconv.Itoa(segundo)
}
// Valido valida CNPJ numérico ou alfanumérico, com ou sem máscara.
func Valido(valor string) bool {
c := Limpar(valor)
if !formato.MatchString(c) {
return false
}
if strings.Count(c, string(c[0])) == len(c) {
return false
}
return CalcularDV(c[:12]) == c[12:]
}
// Formatar aplica a máscara 00.000.000/0000-00.
func Formatar(valor string) string {
c := Limpar(valor)
if len(c) != 14 {
return c
}
return c[:2] + "." + c[2:5] + "." + c[5:8] + "/" + c[8:12] + "-" + c[12:]
}
Testes e integração com o framework
package cnpj
import "testing"
func TestValido(t *testing.T) {
casos := []struct {
entrada string
esperado bool
}{
{"12.ABC.345/01DE-35", true},
{"12abc34501de35", true},
{"11.222.333/0001-81", true},
{"11.222.333/0001-82", false},
{"12ABC34501DEAB", false},
{"00.000.000/0000-00", false},
}
for _, caso := range casos {
if obtido := Valido(caso.entrada); obtido != caso.esperado {
t.Errorf("Valido(%q) = %v, esperado %v", caso.entrada, obtido, caso.esperado)
}
}
}
func TestCalcularDV(t *testing.T) {
if dv := CalcularDV("12ABC34501DE"); dv != "35" {
t.Errorf("CalcularDV = %q, esperado \"35\"", dv)
}
}
func BenchmarkValido(b *testing.B) {
for i := 0; i < b.N; i++ {
Valido("12.ABC.345/01DE-35")
}
}
Detalhes que costumam quebrar em Go
- Indexar uma
stringem Go devolvebyte. Como CNPJ é só ASCII depois da limpeza,sequencia[i]é seguro e mais rápido que converter para[]rune. strings.ToUpperantes da limpeza garante que entradas em minúsculas sejam aceitas.- Para validar em massa, compile as regex uma vez em nível de pacote (como acima) — recompilar dentro do laço custa caro.
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.