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

    Código Python para representar fracciones exactas
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    fractions.from_number: convierte números en fracciones

    Aprende fractions.from_number en Python para convertir números en fracciones exactas, controlar precisión, validar entradas y evitar redondeos inesperados.

    Ler mais

    Tempo de leitura: 4 minutos
    09/10/2026
    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