codecs en Python: domina encodings

Publicado el: 18/08/2026
Tempo de leitura: 6 minutos
Close-up view of a computer screen displaying code in a software development environment.

El módulo codecs en Python ofrece acceso al registro de codificaciones, funciones genéricas para codificar y decodificar, procesadores incrementales, clases de lectura y escritura de streams, constantes BOM y mecanismos para registrar codecs y manejadores de errores personalizados.

La mayoría de los programas no necesita importar codecs para abrir un archivo UTF-8. La función integrada open() y el módulo io son las interfaces recomendadas. El módulo resulta importante cuando una aplicación debe inspeccionar el registro, procesar secuencias parciales, transcodificar streams, usar transformaciones no textuales o implementar una codificación.

Texto y bytes son tipos diferentes

Una cadena de Python contiene puntos de código Unicode. Los archivos y protocolos transportan bytes, por lo que el texto debe codificarse y los bytes deben decodificarse:

texto = "Hola, Python"
datos = texto.encode("utf-8")
restaurado = datos.decode("utf-8")

assert restaurado == texto

El nombre de la codificación forma parte del protocolo. Decodificar bytes UTF-8 como Latin-1 puede producir texto aparentemente válido pero corrupto. No intentes adivinar silenciosamente cuando el productor debería declarar el encoding.

codecs.encode y codecs.decode

import codecs

bytes_utf8 = codecs.encode("precio €", "utf-8", errors="strict")
texto = codecs.decode(bytes_utf8, "utf-8", errors="strict")

print(bytes_utf8)
print(texto)

Estas funciones utilizan el registro y también pueden invocar transformaciones que no están disponibles en str.encode() o bytes.decode(). Para codificaciones de texto comunes, los métodos de los objetos son más directos.

Consulta el registro

import codecs

info = codecs.lookup("utf-8")
print(info.name)
print(info.encode)
print(info.decode)
print(info.incrementalencoder)
print(info.incrementaldecoder)

lookup() resuelve aliases y devuelve un CodecInfo. Un nombre desconocido lanza LookupError. Valida el encoding configurable durante el arranque, no al procesar el primer archivo.

Aliases y nombres normalizados

Nombres como utf-8, UTF_8 y utf8 normalmente apuntan al mismo codec. Espacios y guiones se normalizan. Aun así, utiliza nombres consistentes y documentados. CPython optimiza algunos aliases comunes, mientras variantes inusuales pueden perder esas optimizaciones.

Usa open para archivos de texto

from pathlib import Path

ruta = Path("informe.txt")

with ruta.open("w", encoding="utf-8", newline="\n") as archivo:
    archivo.write("línea 1\nlínea 2\n")

with ruta.open("r", encoding="utf-8") as archivo:
    contenido = archivo.read()

codecs.open() está deprecado desde Python 3.14 y ha sido reemplazado por open(). La API moderna integra buffering, manejo de newlines y la infraestructura de io.

Migra codecs.open

# Antiguo
import codecs
archivo = codecs.open("datos.txt", "r", encoding="utf-8")

# Actual
archivo = open("datos.txt", "r", encoding="utf-8")

Revisa las diferencias de newline durante la migración. Cuando codecs.open() recibe un encoding, el archivo subyacente se abre en binario y no realiza conversión automática de \n. Prueba archivos de Windows y Unix.

Manejo de errores

La política predeterminada strict lanza UnicodeEncodeError o UnicodeDecodeError. Es la opción más segura cuando no se acepta pérdida.

datos = b"nombre: Jos\xe9"

try:
    texto = datos.decode("utf-8", errors="strict")
except UnicodeDecodeError as error:
    print(error.start, error.end, error.reason)

Los handlers principales son:

  • strict: lanza una excepción.
  • replace: inserta un carácter de reemplazo o interrogación.
  • ignore: descarta datos inválidos.
  • backslashreplace: conserva información como escapes.
  • surrogateescape: permite round trip de bytes del sistema.
  • xmlcharrefreplace: genera referencias numéricas.
  • namereplace: utiliza nombres Unicode en escapes.

Evita errors ignore

errors="ignore" puede eliminar letras, separadores, números o caracteres relevantes para seguridad sin aviso. Convierte datos corruptos en datos aparentemente válidos. Úsalo solo cuando la pérdida sea intencional, medible y no cambie significado.

Conserva bytes desconocidos

datos = b"archivo_\xff.txt"
texto = datos.decode("utf-8", errors="surrogateescape")
restaurado = texto.encode("utf-8", errors="surrogateescape")
assert restaurado == datos

La cadena intermedia contiene puntos surrogate. Su finalidad es devolver los bytes a la misma interfaz del sistema; no la envíes sin control a JSON, bases de datos o interfaces.

Registra un handler de errores

import codecs
import logging

logger = logging.getLogger(__name__)

def registrar_y_reemplazar(error):
    logger.warning("fallo de encoding entre %d y %d", error.start, error.end)
    return ("?", error.end)

codecs.register_error("app_registrar_reemplazar", registrar_y_reemplazar)
resultado = "precio: €".encode("ascii", errors="app_registrar_reemplazar")

El handler debe avanzar la posición para no crear un bucle infinito. Evita registrar contenido sensible y usa nombres con prefijo del proyecto.

Decodificación incremental

Un carácter UTF-8 puede quedar dividido entre bloques. Decodificar cada bloque de forma independiente puede fallar. Un decoder incremental guarda secuencias incompletas:

import codecs

decoder = codecs.getincrementaldecoder("utf-8")(errors="strict")
partes = []

for bloque in recibir_bloques():
    partes.append(decoder.decode(bloque, final=False))

partes.append(decoder.decode(b"", final=True))
texto = "".join(partes)

La llamada final procesa el estado pendiente y lanza error si el stream termina con una secuencia incompleta.

Codificación incremental

import codecs

encoder = codecs.getincrementalencoder("utf-16")()
salida = bytearray()

for fragmento in generar_texto():
    salida.extend(encoder.encode(fragmento, final=False))

salida.extend(encoder.encode("", final=True))

Las codificaciones con estado pueden emitir marcadores o conservar información entre llamadas. No crees un encoder nuevo por fragmento.

iterencode e iterdecode

import codecs

bloques_texto = ["Hola ", "mundo", "!\n"]
for bloque in codecs.iterencode(bloques_texto, "utf-8"):
    enviar(bloque)

bloques_bytes = [b"Ol\xc3", b"\xa1 mundo"]
texto = "".join(codecs.iterdecode(bloques_bytes, "utf-8"))

iterencode() requiere cadenas; iterdecode() requiere bytes. Algunas transformaciones binarias o texto-a-texto no son compatibles.

Archivos de texto grandes

El objeto devuelto por open() ya usa un decoder incremental. Iterar por líneas evita cargar todo. Las técnicas de la guía para leer archivos gigantes con Python siguen siendo válidas: bloques, límites, streaming y salida incremental.

BOM en UTF-16 y UTF-32

UTF-16 y UTF-32 pueden usar un Byte Order Mark para identificar endianness. El módulo expone constantes como BOM_UTF16_LE, BOM_UTF16_BE, BOM_UTF32_LE y BOM_UTF32_BE.

import codecs

datos = codecs.BOM_UTF16_LE + "texto".encode("utf-16-le")
print(datos.startswith(codecs.BOM_UTF16_LE))

El codec utf-16 interpreta o genera un BOM. Las variantes explícitas utf-16-le y utf-16-be definen el orden y no deben depender de una marca implícita.

UTF-8 con firma

UTF-8 no necesita orden de bytes, pero algunos programas escriben EF BB BF como firma. utf-8-sig elimina el prefijo al leer y lo añade al escribir:

from pathlib import Path

texto = Path("planilla.csv").read_text(encoding="utf-8-sig")

Úsalo para interoperar con software que espera BOM. En protocolos nuevos, prefiere UTF-8 normal y declara la codificación externamente.

Detectar encoding es incierto

No existe un algoritmo perfecto para identificar la codificación de bytes arbitrarios. Muchas secuencias son válidas en varias páginas de código. Prefiere metadatos, cabeceras, contratos o configuración validada. Las heurísticas deben informar confianza y permitir revisión.

Transcodifica archivos

from pathlib import Path

origen = Path("legado.txt")
destino = Path("nuevo.txt")

with origen.open("r", encoding="latin-1", errors="strict") as entrada:
    with destino.open("w", encoding="utf-8", newline="") as salida:
        for linea in entrada:
            salida.write(linea)

Transcodificar significa decodificar y luego codificar. No reemplaces bytes directamente. Escribe en un archivo temporal y renómbralo tras el éxito.

Codecs personalizados

codecs.register() añade una función de búsqueda que recibe un nombre normalizado y devuelve CodecInfo o None. unregister(), añadido en Python 3.10, la elimina y limpia la caché.

Un codec completo puede definir funciones stateless, clases incrementales y factories de stream. Impleméntalo solo para un formato real compartido; una función normal es más sencilla para una transformación privada.

Transformaciones binarias en el registro

import codecs

codificado = codecs.encode(b"datos", "base64_codec")
restaurado = codecs.decode(codificado, "base64_codec")

El registro incluye transformaciones bytes-a-bytes como Base64, hex, bz2 y zlib. Para Base64 de aplicación, base64 en Python es más claro y permite validación estricta. Para compresión, usa los módulos específicos.

Encoding no es un layout binario

Una codificación de texto convierte caracteres en bytes. No define campos, números ni cabeceras. Usa struct en Python para registros binarios versionados con endianness y longitudes explícitas.

Encodings configurables

Cuando el nombre viene de configuración, valídalo con codecs.lookup() al iniciar. configparser en Python puede cargarlo, pero utiliza una allowlist cuando solo se admiten codificaciones de texto, porque el registro también contiene transformaciones binarias.

Seguridad y límites

  • Limita bytes de origen y texto resultante.
  • Usa strict para datos críticos.
  • Evita detección automática sin metadatos.
  • No expongas surrogates en formatos externos.
  • Finaliza los decoders incrementales.
  • No registres contenido sensible.
  • Usa allowlist de encodings.
  • Limita entradas IDNA y Punycode no confiables.

Pruebas recomendadas

Prueba ASCII, acentos, emoji, caracteres no representables, secuencias UTF-8 divididas, último bloque incompleto, BOM correcto e invertido, firma UTF-8, archivos vacíos, newlines de Windows y Unix, handlers, aliases, round trip y bytes esperados.

Buenas prácticas

  • Usa UTF-8 en formatos nuevos.
  • Declara el encoding en el protocolo.
  • Usa open() en lugar de codecs.open().
  • Prefiere strict y maneja fallos.
  • Usa decoder incremental para chunks manuales.
  • Valida nombres del registro.
  • Mantén texto como str y transporte como bytes.
  • Transcodifica en streaming.

Conclusión

codecs en Python es la infraestructura que conecta nombres de encoding, funciones, streams, políticas de error y procesamiento incremental. Es especialmente útil en bibliotecas, protocolos e interoperabilidad; los archivos de texto normales deben continuar usando open().

Consulta la documentación oficial de codecs y el Unicode Standard. La codificación correcta debe proceder de un contrato, no de prueba y error.

Compartilhe:

Facebook
WhatsApp
Twitter
LinkedIn

Contenido del artículo

    Artículos relacionados

    Código de error sobre datos binarios que representa fallos manejados con urllib.error en Python
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    urllib.error en Python: maneja errores HTTP

    Aprende urllib.error en Python para manejar URLError, HTTPError, descargas incompletas, retries selectivos y diagnósticos de red claros.

    Ler mais

    Tempo de leitura: 5 minutos
    21/08/2026
    Teclas con la palabra HTML que representan entidades HTML en Python
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    html.entities: convierte entidades HTML

    Aprende html.entities en Python para consultar entidades HTML, convertir nombres y code points y no confundir decodificación con sanitización.

    Ler mais

    Tempo de leitura: 7 minutos
    21/08/2026
    Carpeta con archivos que representa tipos MIME identificados con mimetypes en Python
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    mimetypes: detecta tipos MIME de archivos

    Aprende mimetypes en Python para identificar tipos de archivo, validar cargas y definir Content-Type con más seguridad.

    Ler mais

    Tempo de leitura: 6 minutos
    20/08/2026
    Código HTML en una pantalla que representa análisis con html.parser en Python
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    html.parser en Python: analiza HTML

    Aprende html.parser en Python para extraer texto, enlaces y metadatos, procesar HTML por bloques y no confundir parsing con sanitización.

    Ler mais

    Tempo de leitura: 4 minutos
    20/08/2026
    Persona usando un portátil en una sesión web que representa cookies con http.cookiejar en Python
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    http.cookiejar en Python: gestiona cookies

    Aprende http.cookiejar en Python para mantener sesiones, aplicar políticas, persistir cookies de forma segura e integrar urllib.request.

    Ler mais

    Tempo de leitura: 5 minutos
    20/08/2026
    Rack de servidores que representa conexiones HTTP de bajo nivel con http.client en Python
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    http.client en Python: HTTP de bajo nivel

    Aprende http.client en Python para controlar conexiones HTTP y HTTPS, streaming, headers, TLS, reutilización, límites y errores.

    Ler mais

    Tempo de leitura: 4 minutos
    20/08/2026