unicodedata no Python: normalize Unicode

Publicado em: 11/08/2026
Tempo de leitura: 6 minutos
Alfabeto tridimensional representando normalização Unicode com unicodedata no Python

O módulo unicodedata oferece acesso à base de dados oficial de caracteres Unicode usada pela versão do Python em execução. Com ele, uma aplicação pode descobrir nomes, categorias, valores numéricos, classes bidirecionais, caracteres combinantes, decomposições e formas normalizadas. Essas informações são essenciais em buscas, importação de dados, validação, geração de slugs, comparação de textos e internacionalização.

Este guia explica as funções principais e mostra como normalizar sem destruir informações importantes. A documentação do Python 3.14.6 informa que o módulo usa a Unicode Character Database 16.0.0.

Consultar a versão da base Unicode

import unicodedata

print(unicodedata.unidata_version)

A versão pode afetar nomes, categorias e caracteres reconhecidos. Sistemas que persistem resultados de normalização ou validação devem registrar a versão do Python e testar upgrades.

Obter o nome de um caractere

name() devolve o nome oficial. Alguns caracteres não têm nome, então use um valor padrão quando entradas arbitrárias forem esperadas.

import unicodedata

print(unicodedata.name("½"))
print(unicodedata.name("\uFFFF", "SEM NOME"))

Os nomes ajudam em depuração e relatórios. Não são traduções para o idioma do usuário, mas identificadores padronizados do Unicode.

Buscar um caractere pelo nome

lookup() realiza o caminho inverso. Se o nome não existir, gera KeyError.

chave = unicodedata.lookup("LEFT CURLY BRACKET")
print(chave)

A função também reconhece aliases e sequências nomeadas suportadas pela base. Valide nomes fornecidos por usuários e trate o erro.

Categoria geral

category() retorna um código de duas letras. A primeira indica um grupo amplo, como letra, marca, número, pontuação, símbolo, separador, controle ou não atribuído. A segunda refina o tipo.

for caractere in ["A", "a", "9", "!", " "]:
    print(caractere, unicodedata.category(caractere))

Exemplos incluem Lu para letra maiúscula, Ll para minúscula, Nd para dígito decimal e Zs para separador de espaço. Não use apenas categorias para decidir se um identificador é seguro.

Valores decimal, digit e numeric

Unicode contém diferentes conceitos numéricos. decimal() trata dígitos decimais, digit() inclui outros dígitos e numeric() abrange valores como frações e numerais.

print(unicodedata.decimal("٩"))
print(unicodedata.digit("⁹"))
print(unicodedata.numeric("½"))

Os retornos podem ser inteiros ou floats. Para validar números de entrada, defina claramente se aceita apenas ASCII, dígitos decimais internacionais ou qualquer caractere numérico.

Caracteres combinantes

combining() devolve a classe combinante canônica. Zero geralmente indica que o caractere não é uma marca combinante com classe definida.

texto = "a\u0301"
for c in texto:
    print(repr(c), unicodedata.name(c), unicodedata.combining(c))

A sequência contém a letra a e um acento agudo separado. Visualmente pode parecer igual a á, mas as strings podem ter comprimentos e bytes diferentes.

Por que normalizar Unicode

Unicode permite representações canonicamente equivalentes. Uma letra acentuada pode ser um caractere pré-composto ou uma letra seguida de marca combinante. Sem normalização, comparações, chaves de banco e buscas podem falhar.

a = "café"
b = "cafe\u0301"
print(a == b)
print(unicodedata.normalize("NFC", a) == unicodedata.normalize("NFC", b))

Normalizar ambos os lados com a mesma forma melhora consistência, mas não resolve diferenças de caixa, idioma, pontuação ou caracteres visualmente semelhantes.

NFC e NFD

NFD aplica decomposição canônica. NFC decompõe e depois recompõe quando existe uma forma pré-composta.

nfd = unicodedata.normalize("NFD", "ação")
nfc = unicodedata.normalize("NFC", nfd)
print(repr(nfd))
print(repr(nfc))

NFC é uma escolha comum para armazenamento e comparação de texto humano. NFD é útil quando é necessário analisar marcas combinantes ou remover acentos de maneira controlada.

NFKC e NFKD

As formas com K aplicam equivalência de compatibilidade. Elas podem transformar caracteres estilísticos ou históricos em formas mais simples. Por exemplo, numerais romanos e variantes de largura podem mudar.

print(unicodedata.normalize("NFKC", "Ⅳ"))
print(unicodedata.normalize("NFKC", "Full"))

Essa transformação pode perder distinções relevantes. Use NFKC em identificadores e buscas somente após definir uma política. Não a aplique cegamente a assinaturas, senhas, documentos legais ou texto que precisa preservar grafia.

Verificar se já está normalizado

is_normalized() testa NFC, NFD, NFKC ou NFKD sem exigir que a aplicação compare manualmente o resultado.

texto = "café"
if not unicodedata.is_normalized("NFC", texto):
    texto = unicodedata.normalize("NFC", texto)

Em muitos casos, normalizar diretamente é suficientemente simples. A verificação pode ajudar em auditorias e métricas.

Remover acentos com cuidado

Um padrão comum é decompor com NFD e remover marcas. Isso não é transliteração universal e pode alterar palavras ou idiomas.

def sem_acentos(texto):
    decomposto = unicodedata.normalize("NFD", texto)
    filtrado = "".join(
        c for c in decomposto
        if unicodedata.category(c) != "Mn"
    )
    return unicodedata.normalize("NFC", filtrado)

Use para chaves auxiliares de busca, não para substituir o texto original. Letras como ø, ł e muitos caracteres não se convertem adequadamente por esse método.

Decomposição de um caractere

decomposition() retorna códigos hexadecimais e, em alguns casos, uma tag de compatibilidade.

print(unicodedata.decomposition("Ã"))
print(unicodedata.decomposition("①"))

A função é útil em ferramentas de diagnóstico, mas normalmente normalize() é a API correta para transformar texto.

Texto bidirecional

bidirectional() informa a classe usada pelo algoritmo bidirecional. mirrored() indica símbolos que podem ser espelhados em contextos da direita para a esquerda.

print(unicodedata.bidirectional("٧"))
print(unicodedata.mirrored(">"))

Esses dados não substituem um motor completo de layout. Entradas bidirecionais podem ocultar ordem visual, especialmente em código, logs e nomes de arquivo. Exiba representações escapadas em ferramentas de segurança.

Largura do Leste Asiático

east_asian_width() classifica caracteres como estreitos, largos, fullwidth, halfwidth, ambíguos ou neutros.

for c in "A界F":
    print(c, unicodedata.east_asian_width(c))

A propriedade ajuda, mas não calcula sozinha a largura final de terminal. Combinações, emoji e políticas do terminal ainda influenciam.

Casefold e normalização

Para comparação sem diferenciar caixa, uma prática comum é normalizar e usar casefold().

def chave_busca(texto):
    return unicodedata.normalize("NFKC", texto).casefold()

print(chave_busca("Straße") == chave_busca("STRASSE"))

A ordem e a forma escolhidas dependem do domínio. Guarde também o original e evite usar uma chave simplificada como prova de identidade.

Segurança e caracteres semelhantes

Normalização não transforma todas as letras visualmente semelhantes na mesma coisa. O a latino, o alfa grego e o а cirílico continuam distintos. Ataques de homógrafos podem afetar nomes, domínios e identificadores.

Use políticas de scripts permitidos, bibliotecas especializadas e revisão visual para contextos de segurança. Não crie filtros baseados apenas em remover caracteres desconhecidos.

Exemplo de preparação de texto para busca

def preparar_busca(texto):
    texto = unicodedata.normalize("NFKC", texto)
    texto = texto.casefold()
    return " ".join(texto.split())

O exemplo reduz diferenças de compatibilidade, caixa e espaços. A função deve ser versionada como parte do esquema de índice, pois mudanças na base Unicode podem alterar resultados.

Erros frequentes

  • Comparar texto sem uma forma normalizada definida.
  • Usar NFKC quando a aparência original precisa ser preservada.
  • Remover marcas e substituir o conteúdo original.
  • Confundir caracteres numéricos com dígitos ASCII.
  • Esperar que normalização resolva homógrafos.
  • Usar largura asiática como largura visual completa.
  • Ignorar a versão da base Unicode.

Boas práticas

  • Preserve sempre o texto original.
  • Defina a normalização por campo e finalidade.
  • Normalize os dois lados da comparação.
  • Versione chaves e índices derivados.
  • Teste idiomas e scripts relevantes.
  • Use casefold() para comparação caseless.
  • Aplique controles adicionais em identificadores sensíveis.

Conteúdos relacionados

Veja textwrap, locale, fnmatch, pydoc e linecache.

Consulte a documentação oficial do unicodedata e o Unicode HOWTO.

Conclusão

unicodedata permite tratar texto internacional com regras explícitas e reproduzíveis. A normalização correta melhora buscas e comparações, mas não substitui políticas de idioma, segurança, identidade ou apresentação. Preserve o original e escolha cada transformação de acordo com o domínio.

Compartilhe:

Facebook
WhatsApp
Twitter
LinkedIn

Conteúdo do artigo

    Artigos relacionados

    Desenvolvedor configurando servidor HTTPS e certificado TLS com Python
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    HTTPSServer: crie servidor HTTPS local no Python

    Aprenda HTTPSServer no Python para servir HTTPS localmente, configurar certificados, usar threads e entender limites de segurança.

    Ler mais

    Tempo de leitura: 5 minutos
    08/10/2026
    Arquivos protegidos representando extração segura de TAR com Python
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    tarfile extraction_filter: extraia TAR com segurança

    Aprenda tarfile extraction_filter no Python para extrair arquivos TAR com validação, segurança e controle de caminhos.

    Ler mais

    Tempo de leitura: 6 minutos
    08/10/2026
    Código Python exibindo avisos controlados com catch_warnings
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    catch_warnings: capture warnings em testes Python

    Aprenda catch_warnings no Python para capturar, testar e controlar avisos com filtros específicos e segurança.

    Ler mais

    Tempo de leitura: 5 minutos
    07/10/2026
    Código Python representando referências persistentes do pickle
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    pickle persistent_id: serialize referências externas

    Aprenda pickle persistent_id no Python para serializar referências externas com IDs estáveis, validação, segurança e compatibilidade.

    Ler mais

    Tempo de leitura: 5 minutos
    07/10/2026
    Código binário representando buffers e memória no Python
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    memoryview.count: conte valores sem copiar buffers

    Aprenda memoryview.count no Python para contar bytes e valores em buffers sem cópias, com formatos, limites e boas práticas.

    Ler mais

    Tempo de leitura: 6 minutos
    06/10/2026
    Código Python com anotações de tipos
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    annotationlib: evite imports circulares em anotações

    Aprenda annotationlib no Python para ler anotações adiadas, evitar imports circulares e criar ferramentas de introspecção seguras.

    Ler mais

    Tempo de leitura: 6 minutos
    06/10/2026