stringprep en Python: prepara Unicode

Publicado el: 12/08/2026
Tempo de leitura: 5 minutos
Protocolo seguro de Internet que representa preparación Unicode con stringprep en Python

El módulo stringprep expone las tablas definidas por el RFC 3454 para preparar cadenas Unicode utilizadas en protocolos de Internet. La preparación puede mapear o eliminar caracteres, normalizar texto, rechazar categorías prohibidas y aplicar reglas bidireccionales antes de comparar, guardar o transmitir identificadores.

El módulo no ofrece una función universal de limpieza. Proporciona funciones de consulta y mapeo. Cada perfil de protocolo debe definir qué tablas usa, si aplica case folding, qué normalización necesita y cómo trata caracteres no asignados.

Un módulo basado en un estándar histórico

El RFC 3454 se utilizó en perfiles como Nameprep para sistemas tempranos de dominios internacionalizados. Sus tablas se basan en Unicode 3.2, de modo que caracteres modernos pueden aparecer como no asignados en este modelo histórico.

No inventes un perfil para autenticación, dominios o identificadores sensibles. Usa el estándar actual y una biblioteca dedicada cuando exista. stringprep resulta útil para interoperabilidad legada, auditoría y comprensión del proceso.

Tablas expuestas como funciones

Las tablas son demasiado grandes para representarlas como diccionarios normales, por lo que Python ofrece funciones características para conjuntos y funciones de mapeo para transformaciones.

import stringprep

caracter = "\u00ad"  # soft hyphen
print(stringprep.in_table_b1(caracter))

La tabla B.1 incluye caracteres comúnmente mapeados a nada. La consulta no modifica el texto; la aplicación debe realizar la transformación.

Puntos no asignados

in_table_a1() indica si un punto estaba sin asignar en Unicode 3.2.

def tiene_no_asignado(texto):
    return any(stringprep.in_table_a1(c) for c in texto)

Un perfil puede prohibirlos para evitar cambios de significado tras futuras asignaciones. Sin embargo, caracteres modernos también pueden ser considerados no asignados por esta tabla histórica.

Mapear caracteres a nada

def aplicar_b1(texto):
    return "".join(
        c for c in texto
        if not stringprep.in_table_b1(c)
    )

Eliminar caracteres puede hacer que dos entradas distintas converjan. Detecta colisiones después de todo el proceso y conserva el valor original para presentación y auditoría.

Case folding con B.2 y B.3

map_table_b2() proporciona case folding pensado para NFKC. map_table_b3() se usa sin normalización.

def mapear_b2(texto):
    return "".join(stringprep.map_table_b2(c) for c in texto)

Un mapeo puede devolver varios caracteres. No supongas una relación uno a uno ni valides longitud antes de transformar.

Normalización NFKC

Muchos perfiles históricos aplican NFKC después del mapeo.

import unicodedata

def preparar_base(texto):
    texto = aplicar_b1(texto)
    texto = mapear_b2(texto)
    return unicodedata.normalize("NFKC", texto)

NFKC elimina diferencias de compatibilidad y puede cambiar variantes de ancho, caracteres estilísticos y símbolos. Aplícala solo cuando el protocolo lo exija y conserva el original.

Espacios ASCII y no ASCII

Las tablas C.1.1 y C.1.2 identifican espacios ASCII y no ASCII. in_table_c11_c12() comprueba la unión.

espacios = [
    c for c in texto
    if stringprep.in_table_c11_c12(c)
]

Un perfil puede mapearlos, prohibirlos o permitir solo algunos. No sustituyas automáticamente todos por espacio común sin consultar la especificación.

Caracteres de control

C.2.1 y C.2.2 cubren controles ASCII y no ASCII.

def tiene_control(texto):
    return any(
        stringprep.in_table_c21_c22(c)
        for c in texto
    )

Los controles pueden alterar logs, terminales y framing de protocolos. Escapa caracteres invisibles en diagnósticos aunque el perfil permita algunos.

Uso privado, noncharacters y surrogates

in_table_c3(), in_table_c4() e in_table_c5() identifican uso privado, noncharacters y códigos surrogate.

Recházalos según el perfil y antes de serializar a sistemas que exijan valores escalares Unicode válidos.

Otras categorías prohibidas

C.6 y C.7 marcan caracteres inadecuados para texto plano o representación canónica. C.8 cubre caracteres que cambian propiedades visuales o están obsoletos, y C.9 caracteres de tagging.

Estas categorías muestran por qué strip() más lower() no implementa preparación de protocolos.

Reglas bidireccionales

D.1 identifica caracteres con propiedades R o AL. D.2 identifica caracteres L. Los perfiles RFC 3454 suelen imponer reglas especiales cuando hay texto de derecha a izquierda.

def validar_bidi(texto):
    tiene_randal = any(stringprep.in_table_d1(c) for c in texto)
    if not tiene_randal:
        return True
    if any(stringprep.in_table_d2(c) for c in texto):
        return False
    return (
        stringprep.in_table_d1(texto[0])
        and stringprep.in_table_d1(texto[-1])
    )

El ejemplo muestra la regla histórica básica, pero producción debe seguir exactamente el perfil. El orden lógico y visual puede diferir.

Ejemplo orientado a perfil

import unicodedata

class StringPrepError(ValueError):
    pass

def preparar_legado(texto):
    mapeado = "".join(
        "" if stringprep.in_table_b1(c)
        else stringprep.map_table_b2(c)
        for c in texto
    )
    normalizado = unicodedata.normalize("NFKC", mapeado)

    prohibidas = (
        stringprep.in_table_c12,
        stringprep.in_table_c21_c22,
        stringprep.in_table_c3,
        stringprep.in_table_c4,
        stringprep.in_table_c5,
        stringprep.in_table_c6,
        stringprep.in_table_c7,
        stringprep.in_table_c8,
        stringprep.in_table_c9,
    )
    for c in normalizado:
        if any(tabla(c) for tabla in prohibidas):
            raise StringPrepError(
                f"carácter prohibido U+{ord(c):04X}"
            )

    if not validar_bidi(normalizado):
        raise StringPrepError("regla bidi inválida")
    return normalizado

Es un ejemplo educativo, no un perfil nombrado completo. La lista de tablas, política A.1 y orden deben venir del estándar.

Validar después de transformar

Comprueba límites de caracteres y bytes después del mapeo y la normalización porque la longitud puede cambiar. Si el resultado es una clave única, aplica unicidad transaccional sobre el valor final.

IDNA y dominios

No reconstruyas Nameprep manualmente para dominios modernos. Usa una implementación IDNA acorde al estándar requerido. El procesamiento incluye labels, Punycode y validaciones adicionales.

Contraseñas y usuarios

No apliques stringprep genérico a contraseñas sin especificación. El mapeo o eliminación puede cambiar el secreto y reducir entropía. Protocolos modernos pueden usar perfiles PRECIS u otras reglas.

Para usuarios, conserva el original y una clave preparada separada. Vigila colisiones y caracteres confundibles.

Homógrafos

Case folding y NFKC no unen todos los caracteres visualmente parecidos. Letras latinas, griegas y cirílicas pueden seguir siendo distintas. Identificadores públicos sensibles necesitan políticas de scripts y detección especializada.

Pruebas

Prueba controles, espacios no ASCII, eliminaciones B.1, expansión de case folding, texto RTL, mezcla L/R, entrada vacía y caracteres modernos. Compara con vectores oficiales del protocolo.

Versiona el algoritmo. Cambiarlo para identificadores existentes requiere migración y análisis de colisiones.

Errores frecuentes

  • Creer que el módulo ofrece un sanitizer completo.
  • Crear un perfil sin estándar.
  • Ignorar la base Unicode 3.2.
  • Comprobar tamaño antes del mapeo.
  • No detectar colisiones.
  • Aplicar NFKC o eliminación a contraseñas arbitrariamente.
  • Ignorar bidi y homógrafos.

Buenas prácticas

  • Usa una biblioteca específica del protocolo.
  • Conserva la entrada original.
  • Versiona la función de preparación.
  • Sigue el orden de la especificación.
  • Valida longitud y unicidad al final.
  • Escapa caracteres invisibles en logs.
  • Prueba vectores oficiales y Unicode adversarial.

Guías relacionadas

Continúa con unicodedata en Python, locale en Python, textwrap en Python, fnmatch en Python y contextvars en Python.

Consulta la documentación oficial de stringprep y el RFC 3454.

Conclusión

stringprep es una caja de herramientas de bajo nivel para perfiles históricos de preparación Unicode. Debe usarse bajo una especificación definida, con conciencia de Unicode 3.2, colisiones, categorías prohibidas y reglas bidi. Para sistemas modernos, prefiere implementaciones mantenidas del protocolo.

Compartilhe:

Facebook
WhatsApp
Twitter
LinkedIn

Contenido del artículo

    Artículos relacionados

    Red de conexiones que representa I/O no bloqueante con selectors en Python
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    selectors en Python: I/O no bloqueante

    Aprende selectors en Python para monitorizar muchos sockets, eventos de lectura y escritura, timeouts y conexiones no bloqueantes con seguridad.

    Ler mais

    Tempo de leitura: 4 minutos
    11/08/2026
    Flujo de datos en red que representa contexto asíncrono con contextvars en Python
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    contextvars en Python: contexto asíncrono

    Aprende contextvars en Python para guardar estado por tarea, evitar fugas en asyncio, copiar contextos y restaurar valores con tokens.

    Ler mais

    Tempo de leitura: 5 minutos
    11/08/2026
    Código de programación que representa operaciones como funciones con operator en Python
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    operator en Python: operaciones como funciones

    Aprende operator en Python para usar operaciones como funciones, ordenar campos, acceder a elementos, llamar métodos y crear pipelines claros.

    Ler mais

    Tempo de leitura: 4 minutos
    11/08/2026
    Alfabeto tridimensional que representa normalización Unicode con unicodedata en Python
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    unicodedata en Python: normaliza Unicode

    Aprende unicodedata en Python para normalizar Unicode, consultar nombres, categorías, números, marcas combinantes y ancho de visualización.

    Ler mais

    Tempo de leitura: 5 minutos
    11/08/2026
    Red de servidores que representa la gestión de recursos con ExitStack en Python
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    ExitStack en Python: gestiona recursos

    Aprende ExitStack en Python para gestionar archivos, conexiones, callbacks y limpieza dinámica con seguridad y orden predecible.

    Ler mais

    Tempo de leitura: 6 minutos
    11/08/2026
    Carpeta con candado que representa tipos y permisos con stat en Python
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    stat en Python: tipos y permisos

    Aprende stat en Python para interpretar tipos de archivo, permisos, enlaces, timestamps, atributos de Windows y flags de Unix con

    Ler mais

    Tempo de leitura: 5 minutos
    10/08/2026