unicodedata en Python: normaliza Unicode

Publicado el: 11/08/2026
Tempo de leitura: 5 minutos
Alfabeto tridimensional que representa normalización Unicode con unicodedata en Python

El módulo unicodedata permite acceder a la base oficial de caracteres Unicode integrada en la versión de Python en ejecución. Una aplicación puede consultar nombres, categorías, valores numéricos, clases bidireccionales, marcas combinantes, descomposiciones y formas normalizadas. Estas funciones son esenciales en búsquedas, importación de datos, identificadores, slugs, comparaciones, validación e internacionalización.

Esta guía explica las APIs principales y muestra cómo normalizar sin destruir información. Python 3.14.6 documenta la Unicode Character Database 16.0.0.

Consultar la versión Unicode

import unicodedata

print(unicodedata.unidata_version)

La versión puede cambiar nombres, categorías y caracteres reconocidos. Los sistemas que guardan claves normalizadas deben registrar la versión de Python y probar las actualizaciones.

Obtener el nombre de un carácter

name() devuelve el nombre oficial. Algunos caracteres no tienen nombre asignado, por lo que conviene proporcionar un valor por defecto.

print(unicodedata.name("½"))
print(unicodedata.name("\uFFFF", "SIN NOMBRE"))

Los nombres sirven para diagnóstico e informes. Son identificadores estandarizados, no traducciones para el usuario.

Buscar por nombre

lookup() realiza la operación inversa y genera KeyError si el nombre no existe.

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

También admite aliases y secuencias nombradas incluidas en la base. Valida nombres de usuarios y trata el error.

Categoría general

category() devuelve un código de dos letras. La primera indica un grupo amplio, como letras, marcas, números, puntuación, símbolos, separadores, controles o caracteres no asignados.

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

Ejemplos: Lu para mayúscula, Ll para minúscula, Nd para dígito decimal y Zs para separador de espacio. Las categorías no son una política completa de seguridad.

decimal, digit y numeric

Unicode distingue varios conceptos numéricos. decimal() trata dígitos decimales, digit() incluye otras formas y numeric() cubre fracciones y numerales.

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

Define si un campo acepta solo ASCII, dígitos decimales internacionales o cualquier carácter numérico Unicode.

Marcas combinantes

combining() devuelve la clase combinante canónica. Cero suele indicar que no existe una clase combinante definida.

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

La secuencia contiene una letra y un acento separado. Puede verse igual que á aunque tenga longitud y bytes distintos.

Por qué normalizar

Unicode permite representaciones canónicamente equivalentes. Sin normalización, comparaciones, claves de base, cachés e índices pueden tratar textos visualmente iguales como distintos.

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

Normalizar no resuelve diferencias de caja, puntuación, idioma ni caracteres visualmente confundibles.

NFC y NFD

NFD aplica descomposición canónica. NFC descompone y luego recompone cuando existe una forma precompuesta.

nfd = unicodedata.normalize("NFD", "acción")
nfc = unicodedata.normalize("NFC", nfd)

NFC es una opción común para guardar y comparar texto humano. NFD resulta útil para analizar marcas o crear una clave auxiliar sin acentos.

NFKC y NFKD

Las formas de compatibilidad pueden sustituir variantes estilísticas o históricas por equivalentes simples.

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

Estas transformaciones pueden perder distinciones. Úsalas en búsqueda o identificadores solo con una política definida. No las apliques ciegamente a contraseñas, firmas, documentos legales o contenido tipográfico.

Comprobar la forma

is_normalized() comprueba NFC, NFD, NFKC o NFKD.

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

Normalizar directamente suele ser suficiente; la verificación ayuda en auditorías y métricas.

Quitar acentos con cuidado

Un patrón habitual descompone y elimina marcas sin espaciado. No es una transliteración universal.

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

Usa el resultado como clave auxiliar, no como reemplazo del original. Muchas letras no se convierten correctamente así.

Descomposición

decomposition() devuelve puntos de código hexadecimales y a veces una etiqueta de compatibilidad.

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

Es útil para diagnóstico. Para transformar texto normalmente se debe usar normalize().

Texto bidireccional

bidirectional() devuelve la clase bidi y mirrored() identifica símbolos que pueden reflejarse en texto de derecha a izquierda.

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

Estas propiedades no sustituyen un motor de layout. Controles bidireccionales pueden ocultar el orden visual en código, logs y archivos; las herramientas de seguridad deben ofrecer vistas escapadas.

Ancho asiático

east_asian_width() clasifica caracteres como estrechos, anchos, fullwidth, halfwidth, ambiguos o neutros.

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

La propiedad ayuda, pero no calcula por sí sola el ancho final de terminal. Marcas, emojis y configuración siguen influyendo.

Casefold y normalización

Para comparación sin caja, normaliza y usa casefold().

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

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

Conserva siempre el original y no uses una clave simplificada como prueba de identidad.

Caracteres confundibles

La normalización no une todos los caracteres visualmente similares. Letras latinas, griegas y cirílicas pueden parecer iguales y seguir siendo distintas. Los homógrafos afectan usuarios, dominios, paquetes e identificadores.

Usa restricciones de scripts, detección especializada y revisión humana en contextos sensibles.

Preparar una clave de búsqueda

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

Versiona esta función como parte del esquema del índice, porque una actualización Unicode puede cambiar resultados.

Errores frecuentes

  • Comparar texto sin una forma normalizada.
  • Usar NFKC donde importa conservar distinciones.
  • Reemplazar el original por una copia sin acentos.
  • Tratar cualquier número Unicode como dígito ASCII.
  • Esperar que normalizar evite homógrafos.
  • Usar ancho asiático como cálculo visual completo.
  • Ignorar la versión Unicode.

Buenas prácticas

  • Conserva siempre la entrada original.
  • Define normalización por campo y finalidad.
  • Normaliza ambos lados de la comparación.
  • Versiona claves e índices derivados.
  • Prueba idiomas y scripts relevantes.
  • Usa casefold() para comparación caseless.
  • Aplica controles extra en identificadores sensibles.

Guías relacionadas

Continúa con textwrap en Python, locale en Python, fnmatch en Python, pydoc en Python y linecache en Python.

Consulta la documentación oficial de unicodedata y el Unicode HOWTO.

Conclusión

unicodedata permite tratar texto internacional con reglas explícitas y reproducibles. La normalización mejora búsquedas y comparaciones, pero no sustituye políticas de idioma, identidad, seguridad o presentación. Conserva el original y elige cada transformación según el dominio.

Compartilhe:

Facebook
WhatsApp
Twitter
LinkedIn

Contenido del artículo

    Artículos relacionados

    Desarrollador configurando un servidor HTTPS y certificado TLS con Python
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    HTTPSServer: crea un servidor HTTPS local en Python

    Aprende HTTPSServer en Python para crear servicios HTTPS locales, configurar certificados, usar hilos y comprender sus límites.

    Ler mais

    Tempo de leitura: 4 minutos
    08/10/2026
    Archivos protegidos que representan extracción segura de TAR con Python
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    tarfile extraction_filter: extrae TAR con seguridad

    Aprende tarfile extraction_filter en Python para extraer archivos TAR con validación, seguridad y control de rutas.

    Ler mais

    Tempo de leitura: 6 minutos
    08/10/2026
    Código Python mostrando avisos controlados con catch_warnings
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    catch_warnings: captura warnings en pruebas Python

    Aprende catch_warnings en Python para capturar, probar y controlar avisos con filtros específicos y alcance seguro.

    Ler mais

    Tempo de leitura: 5 minutos
    07/10/2026
    Código Python que representa referencias persistentes de pickle
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    pickle persistent_id: serializa referencias externas

    Aprende pickle persistent_id en Python para referencias externas estables, validación, seguridad, rendimiento y compatibilidad.

    Ler mais

    Tempo de leitura: 5 minutos
    07/10/2026
    Código binario que representa buffers y vistas de memoria en Python
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    memoryview.count: cuenta valores sin copiar buffers

    Aprende memoryview.count en Python para contar bytes y valores en buffers sin copias, con formatos, límites y buenas prácticas.

    Ler mais

    Tempo de leitura: 6 minutos
    06/10/2026
    Código Python y anotaciones de tipos
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    annotationlib: evita imports circulares en anotaciones

    Aprende annotationlib en Python para inspeccionar anotaciones diferidas, evitar imports circulares y crear herramientas seguras.

    Ler mais

    Tempo de leitura: 6 minutos
    06/10/2026