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

    Documento y bandeja de entrada que representan buzones de correo con mailbox en Python
    Bibliotecas y Módulos
    Foto de perfil de Leandro Hirt da Academify

    mailbox en Python: buzones de correo

    Aprende mailbox en Python para leer, crear y migrar Maildir, mbox y MH con locking, flags, mensajes y manejo seguro

    Ler mais

    Tempo de leitura: 5 minutos
    12/08/2026
    Editor de texto que representa formato con textwrap en Python
    Bibliotecas y Módulos
    Foto de perfil de Leandro Hirt da Academify

    textwrap en Python: formatea textos

    Aprende textwrap en Python para dividir, rellenar, acortar, indentar y quitar sangrías con control de ancho, espacios y palabras largas.

    Ler mais

    Tempo de leitura: 5 minutos
    10/08/2026
    Carpeta y lupa que representan filtros de nombres con fnmatch en Python
    Bibliotecas y Módulos
    Foto de perfil de Leandro Hirt da Academify

    fnmatch en Python: filtra nombres de archivos

    Aprende fnmatch en Python para filtrar nombres de archivos con comodines, controlar mayúsculas, excluir patrones y distinguir glob de regex.

    Ler mais

    Tempo de leitura: 5 minutos
    10/08/2026
    Monitor con datos binarios que representa arrays numéricos compactos en Python
    Bibliotecas y Módulos
    Foto de perfil de Leandro Hirt da Academify

    array en Python: números compactos

    Aprende array en Python para almacenar números compactos, usar archivos binarios, byte order, memoryview y buffers seguros.

    Ler mais

    Tempo de leitura: 5 minutos
    10/08/2026
    Círculo cromático que representa conversiones RGB, HSV y HLS con colorsys en Python
    Bibliotecas y Módulos
    Foto de perfil de Leandro Hirt da Academify

    colorsys en Python: RGB, HSV y HLS

    Aprende colorsys en Python para convertir colores entre RGB, HSV, HLS y YIQ, crear paletas y evitar errores de escala

    Ler mais

    Tempo de leitura: 5 minutos
    09/08/2026
    Icono de configuración que representa archivos plist con plistlib en Python
    Bibliotecas y Módulos
    Foto de perfil de Leandro Hirt da Academify

    plistlib: lee y escribe archivos plist

    Aprende plistlib en Python para leer y escribir archivos plist XML y binarios, validar datos y manejar fechas, bytes y

    Ler mais

    Tempo de leitura: 6 minutos
    08/08/2026