fileinput en Python: lee varios archivos

Publicado el: 07/08/2026
Tempo de leitura: 7 minutos
Teclado y flujo de datos que representa el procesamiento de varios archivos con fileinput en Python

Los programas de terminal suelen necesitar procesar varios archivos como si fueran un único flujo continuo. También es común leer la entrada estándar cuando no se proporcionan rutas. El módulo fileinput en Python resuelve ambos casos con un solo iterador: obtiene nombres desde sys.argv, usa sys.stdin cuando la lista está vacía y expone información como archivo actual, número acumulado de línea y posición dentro de cada archivo.

En esta guía aprenderás a usar fileinput.input(), la clase FileInput, hooks para archivos comprimidos, encodings explícitos, stdin, reescritura in-place, backups y tratamiento seguro de errores. El contenido complementa nuestras guías sobre pathlib, tempfile, shlex, filecmp y linecache.

Cuándo usar fileinput

El módulo es adecuado para filtros que aplican la misma operación línea por línea a cero, uno o varios archivos.

import fileinput

for linea in fileinput.input(encoding="utf-8"):
    procesar(linea)

Por defecto, los nombres vienen de sys.argv[1:]. Si la lista está vacía, la entrada viene de sys.stdin. El mismo script funciona así con rutas, pipes y redirecciones.

Ejemplos en terminal

python analizar.py app.log api.log
cat app.log | python analizar.py

El primer comando recorre dos archivos. El segundo consume el pipe. Un nombre igual a - también representa stdin.

Proporcionar archivos explícitamente

El código reutilizable no debería depender de los argumentos del proceso cuando ya dispone de una lista de rutas.

archivos = ["enero.csv", "febrero.csv"]

with fileinput.input(files=archivos, encoding="utf-8") as entrada:
    for linea in entrada:
        procesar(linea)

El contexto cierra el archivo actual y la secuencia incluso cuando ocurre una excepción.

Un único nombre también es válido

with fileinput.input(files="datos.txt", encoding="utf-8") as entrada:
    for linea in entrada:
        print(linea, end="")

Usa lista o tupla cuando la colección se construya dinámicamente.

Los saltos se conservan

Las líneas devueltas mantienen el salto final cuando existe. La última línea de un archivo puede no tenerlo.

for linea in fileinput.input(files="datos.txt", encoding="utf-8"):
    print(repr(linea))

No apliques strip() sin pensar porque también elimina espacios y tabulaciones significativos. Para quitar solo terminadores usa una política explícita como rstrip("\r\n").

Nombre del archivo actual

fileinput.filename() devuelve la fuente de la última línea leída.

for linea in fileinput.input(files=archivos, encoding="utf-8"):
    print(fileinput.filename(), linea, end="")

Antes de la primera línea devuelve None. Las funciones globales dependen de una instancia activa creada por fileinput.input().

Número acumulado

lineno() cuenta todas las líneas desde el inicio de la secuencia.

for linea in fileinput.input(files=archivos, encoding="utf-8"):
    print(fileinput.lineno(), linea, end="")

Si el primer archivo tiene 100 líneas, la primera del segundo recibe el número acumulado 101.

Número dentro del archivo

filelineno() reinicia la cuenta en cada fuente.

for linea in fileinput.input(files=archivos, encoding="utf-8"):
    print(
        fileinput.filename(),
        fileinput.filelineno(),
        linea,
        end="",
    )

Este valor es el más útil en mensajes de validación.

Detectar la primera línea

isfirstline() indica si la línea actual es la primera del archivo.

for linea in fileinput.input(files=archivos, encoding="utf-8"):
    if fileinput.isfirstline():
        print(f"--- {fileinput.filename()} ---")
    print(linea, end="")

Puede utilizarse para cabeceras, separadores e inicialización por archivo.

Detectar stdin

isstdin() informa si la última línea provino de la entrada estándar.

origen = "stdin" if fileinput.isstdin() else fileinput.filename()

Si stdin aparece varias veces, las apariciones posteriores normalmente no entregan líneas porque el flujo ya se consumió.

Saltar el resto de un archivo

nextfile() cierra la fuente actual y avanza. Las líneas ignoradas no aumentan la cuenta acumulada.

for linea in fileinput.input(files=archivos, encoding="utf-8"):
    if linea.startswith("FIN"):
        fileinput.nextfile()
        continue
    procesar(linea)

No puede saltar el primer archivo antes de leer alguna línea, y el nombre actual cambia después de obtener datos de la siguiente fuente.

Usar FileInput directamente

La clase ofrece las mismas operaciones sin depender del estado global.

from fileinput import FileInput

with FileInput(files=archivos, encoding="utf-8") as entrada:
    for linea in entrada:
        print(
            entrada.filename(),
            entrada.filelineno(),
            linea,
            end="",
        )

Esta forma es mejor para bibliotecas, servidores, pruebas y programas que necesitan secuencias independientes.

Evitar secuencias globales superpuestas

fileinput.input() instala un estado global. Abrir otra secuencia antes de cerrar la primera genera errores o resultados confusos. Los objetos FileInput hacen visible la propiedad del recurso.

Encoding y errores

Las versiones modernas aceptan encoding y errors.

with fileinput.input(
    files=archivos,
    encoding="utf-8",
    errors="strict",
) as entrada:
    for linea in entrada:
        procesar(linea)

Usa strict cuando texto inválido debe detener el proceso. Políticas como replace o surrogateescape deben documentarse porque alteran o preservan bytes problemáticos de forma distinta.

Modo binario

FileInput permite mode="r" o mode="rb".

with fileinput.FileInput(files=archivos, mode="rb") as entrada:
    for linea in entrada:
        procesar_bytes(linea)

En modo binario las líneas son bytes. No proporciones encoding.

Errores de lectura

Fileinput lanza OSError cuando no puede abrir o leer.

try:
    with fileinput.input(files=archivos, encoding="utf-8") as entrada:
        for linea in entrada:
            procesar(linea)
except OSError as error:
    print(f"fallo de lectura: {error}")

Un lote debe decidir si un archivo defectuoso interrumpe todo o se registra para tratamiento posterior.

Archivos vacíos

Un archivo vacío se abre y cierra sin producir líneas. Si eso es inválido, comprueba el tamaño o registra explícitamente las rutas procesadas.

Leer gzip y bzip2

hook_compressed() abre archivos .gz y .bz2 de forma transparente.

with fileinput.FileInput(
    files=["app.log", "app.log.gz", "viejo.log.bz2"],
    openhook=fileinput.hook_compressed,
    encoding="utf-8",
) as entrada:
    for linea in entrada:
        procesar(linea)

Otras extensiones se abren normalmente. La detección depende del sufijo, no de una firma validada.

Hooks personalizados

Un openhook recibe nombre y modo y devuelve un objeto abierto. Encoding y errors se pasan como keywords cuando existen.

from pathlib import Path


def abrir_validado(filename, mode, *, encoding=None, errors=None):
    ruta = Path(filename).resolve()
    if ruta.suffix not in {".txt", ".log"}:
        raise ValueError("extensión no soportada")
    return open(ruta, mode, encoding=encoding, errors=errors)

with fileinput.FileInput(
    files=archivos,
    openhook=abrir_validado,
    encoding="utf-8",
) as entrada:
    for linea in entrada:
        procesar(linea)

No se puede combinar un hook con edición in-place.

Filtrado in-place

Con inplace=True, el archivo original se mueve a un backup y sys.stdout apunta a la ruta original. Todo lo impreso se convierte en el nuevo contenido.

for linea in fileinput.input(
    files=["config.txt"],
    inplace=True,
    backup=".bak",
    encoding="utf-8",
):
    print(linea.replace("antiguo", "nuevo"), end="")

Al finalizar, config.txt contiene el texto transformado y config.txt.bak conserva el original.

Riesgos de la edición in-place

La operación es destructiva. Un fallo, una ruta equivocada, salida truncada o un print() accidental pueden corromper datos. Prueba la transformación sin inplace, opera sobre copias y conserva backups.

La documentación oficial de fileinput indica que un backup existente con el mismo nombre puede reemplazarse silenciosamente. Los flujos críticos deben generar nombres únicos.

Backup por defecto

Sin un sufijo permanente, fileinput usa un backup temporal y lo elimina al cerrar. Los datos importantes deberían usar una extensión explícita o una política de archivado separada.

Logs durante la reescritura

Stdout está redirigido al archivo. Envía progreso y diagnósticos a stderr.

import sys

print("procesando", fileinput.filename(), file=sys.stderr)

Separa estrictamente salida transformada y mensajes operativos.

In-place y stdin

La edición in-place está desactivada para stdin porque no existe un archivo original. Un filtro por pipe debe escribir a stdout y dejar que el llamador elija el destino.

Reescritura transaccional

Para archivos críticos, escribe en un temporal, realiza flush(), valida el resultado y sustituye el original con os.replace(). Este diseño ofrece más control sobre errores, permisos, backups y recuperación.

Acceso estrictamente secuencial

FileInput no ofrece acceso aleatorio ni índices. Evita mezclar iteración y readline() de forma confusa. Para recuperar una línea arbitraria usa linecache o una estructura propia.

Seguridad de rutas

Valida nombres externos contra una raíz permitida y resuelve symlinks.

BASE = Path("/srv/importaciones").resolve()

def ruta_permitida(nombre):
    ruta = (BASE / nombre).resolve()
    if ruta != BASE and BASE not in ruta.parents:
        raise ValueError("ruta fuera del directorio permitido")
    return ruta

Limita también cantidad, tamaño y tipos aceptados.

Entradas comprimidas hostiles

Un archivo pequeño puede expandirse a muchos gigabytes. Aplica límites de bytes, tiempo y líneas. hook_compressed() facilita la apertura, pero no evita bombas de descompresión.

Pruebas

def test_varios_archivos(tmp_path):
    primero = tmp_path / "a.txt"
    segundo = tmp_path / "b.txt"
    primero.write_text("uno\ndos\n", encoding="utf-8")
    segundo.write_text("tres\n", encoding="utf-8")

    with fileinput.FileInput(
        files=[primero, segundo],
        encoding="utf-8",
    ) as entrada:
        lineas = list(entrada)

    assert lineas == ["uno\n", "dos\n", "tres\n"]

Prueba archivos vacíos, última línea sin salto, encoding inválido, stdin, compresión y fallos durante reescritura.

fileinput frente a open()

Usa open() para un solo archivo y control explícito. Usa fileinput cuando la abstracción principal es una secuencia continua de líneas provenientes de varias fuentes.

Rendimiento

Fileinput procesa una línea a la vez y no carga todos los archivos en memoria. Los costes dominantes suelen ser almacenamiento, decodificación, descompresión y la transformación. Compila expresiones regulares fuera del bucle y mide cargas reales.

Errores frecuentes

  • Depender accidentalmente de sys.argv.
  • Omitir encoding.
  • Abrir secuencias globales superpuestas.
  • Usar strip() y eliminar espacios válidos.
  • Imprimir logs a stdout durante in-place.
  • Reescribir datos sin backup permanente.
  • Suponer que stdin puede consumirse varias veces.
  • Aceptar compresión sin límites.

Buenas prácticas

  • Usa un contexto with.
  • Prefiere objetos FileInput en bibliotecas.
  • Declara encoding y política de errores.
  • Envía diagnósticos a stderr.
  • Conserva backups para cambios destructivos.
  • Valida rutas y tamaños externos.
  • Prueba archivos vacíos y última línea.
  • Usa sustitución atómica para datos críticos.

Conclusión

El módulo fileinput en Python simplifica filtros que leen varios archivos o stdin, manteniendo información útil de origen y numeración. También ofrece hooks para compresión y una reescritura in-place conveniente.

Esa comodidad exige disciplina. Evita estado global en aplicaciones complejas, define las reglas de texto, trata OSError, limita entradas desconocidas y considera cada reescritura como destructiva. Con validación, backups y pruebas, fileinput es una base compacta para utilidades de terminal confiables.

Compartilhe:

Facebook
WhatsApp
Twitter
LinkedIn

Contenido del artículo

    Artículos relacionados

    Editor de código con líneas numeradas que representa el módulo linecache en Python
    Bibliotecas y Módulos
    Foto de perfil de Leandro Hirt da Academify

    linecache en Python: lee líneas por número

    Aprende linecache en Python para leer líneas por número, administrar la caché, actualizar archivos modificados e integrar traceback y loaders.

    Ler mais

    Tempo de leitura: 7 minutos
    07/08/2026
    Datos binarios que representan serialización interna con marshal en Python
    Bibliotecas y Módulos
    Foto de perfil de Leandro Hirt da Academify

    marshal en Python: serialización interna

    Aprende marshal en Python para serializar tipos internos, controlar versiones y bloquear objetos de código cuando no sean necesarios.

    Ler mais

    Tempo de leitura: 6 minutos
    06/08/2026
    Monitor con código binario que representa personalización de pickle con copyreg en Python
    Bibliotecas y Módulos
    Foto de perfil de Leandro Hirt da Academify

    copyreg en Python: personaliza pickle

    Aprende copyreg en Python para registrar funciones de reducción, personalizar pickle y preservar compatibilidad de objetos.

    Ler mais

    Tempo de leitura: 6 minutos
    06/08/2026
    Código Python que representa funciones especializadas con functools.partial
    Bibliotecas y Módulos
    Foto de perfil de Leandro Hirt da Academify

    functools.partial en Python: guía práctica

    Aprende functools.partial en Python para fijar argumentos, adaptar callbacks y crear funciones especializadas claras.

    Ler mais

    Tempo de leitura: 5 minutos
    06/08/2026
    Bibliotecas y Módulos
    Foto de perfil de Leandro Hirt da Academify

    filecmp en Python: compara archivos y carpetas

    Aprende filecmp en Python para comparar archivos y carpetas con shallow, dircmp, cmpfiles, caché y hashes de integridad.

    Ler mais

    Tempo de leitura: 6 minutos
    03/08/2026
    Terminal de comandos que representa parsing seguro con shlex en Python
    Bibliotecas y Módulos
    Foto de perfil de Leandro Hirt da Academify

    shlex en Python: comandos seguros

    Aprende shlex en Python para separar comandos, tratar comillas, usar quote y join y reducir riesgos de inyección de shell.

    Ler mais

    Tempo de leitura: 6 minutos
    02/08/2026