Leer archivos de varios gigabytes con Python puede bloquear un programa si se intenta cargar todo el contenido en memoria. La solución consiste en procesar los datos de forma incremental: línea por línea, por bloques o mediante iteradores. Este enfoque permite trabajar con registros, CSV, JSONL y archivos comprimidos incluso en equipos con recursos limitados.
Antes de continuar, puedes ampliar conceptos con las guías sobre archivos de texto en Python, generadores con yield, manejo de excepciones, logging y archivos CSV. Como referencias externas, consulta la documentación oficial de open() y la documentación del módulo io.
Por qué leer todo el archivo es un problema
El método read() sin tamaño intenta devolver todo el contenido. Si un archivo pesa 8 GB, el programa puede necesitar todavía más memoria por la representación interna de strings, estructuras auxiliares y copias temporales. El sistema operativo comienza a intercambiar memoria con el disco, el proceso se vuelve lento y finalmente puede aparecer MemoryError o un cierre forzado.
Lectura línea por línea
La forma más sencilla y segura para archivos de texto es iterar directamente sobre el objeto abierto. Python mantiene un búfer interno y entrega una línea cada vez.
from pathlib import Path
ruta = Path("servidor.log")
with ruta.open("r", encoding="utf-8") as archivo:
for numero, linea in enumerate(archivo, start=1):
if "ERROR" in linea:
print(numero, linea.rstrip())Este patrón mantiene el consumo de memoria casi constante. También garantiza el cierre del descriptor gracias a la sentencia with.
Procesar por bloques de bytes
Algunos archivos no están organizados por líneas o contienen registros muy largos. En esos casos, utiliza un tamaño fijo y repite la lectura hasta recibir una cadena vacía.
def leer_bloques(ruta, tamano=1024 * 1024):
with open(ruta, "rb") as archivo:
while bloque := archivo.read(tamano):
yield bloque
for bloque in leer_bloques("datos.bin"):
procesar(bloque)Un bloque de un megabyte suele ser un punto de partida razonable, pero conviene medir. Bloques demasiado pequeños aumentan llamadas al sistema; bloques enormes vuelven a elevar el uso de memoria.
Crear un generador de registros
Separar lectura y transformación facilita reutilizar el flujo. Un generador puede limpiar cada línea y producir únicamente registros válidos.
import json
def registros_jsonl(ruta):
with open(ruta, encoding="utf-8") as archivo:
for numero, linea in enumerate(archivo, 1):
linea = linea.strip()
if not linea:
continue
try:
yield json.loads(linea)
except json.JSONDecodeError as error:
print(f"Línea {numero} inválida: {error}")El consumidor decide si filtra, agrega, guarda o envía los objetos. En ningún momento se conserva todo el conjunto.
CSV grandes con el módulo csv
csv.DictReader también funciona como iterador. No necesitas convertirlo a lista, porque eso eliminaría la ventaja del procesamiento incremental.
import csv
with open("ventas.csv", newline="", encoding="utf-8") as archivo:
lector = csv.DictReader(archivo)
total = 0
for fila in lector:
total += int(fila["cantidad"])
print(total)Valida columnas y conversiones. Una fila defectuosa no debería destruir horas de procesamiento; registra el número de línea y decide si omitirla o detener el trabajo.
Pandas con chunksize
Cuando necesitas operaciones de Pandas, utiliza chunksize. Cada fragmento es un DataFrame independiente que puede limpiarse y agregarse.
import pandas as pd
totales = {}
for fragmento in pd.read_csv("ventas.csv", chunksize=100_000):
resumen = fragmento.groupby("categoria")["importe"].sum()
for categoria, valor in resumen.items():
totales[categoria] = totales.get(categoria, 0) + valorEl tamaño ideal depende del número de columnas, tipos y memoria disponible. Observa el proceso con datos reales en lugar de escoger un valor arbitrario.
Archivos comprimidos sin extracción completa
Los módulos gzip, bz2 y lzma exponen interfaces similares a open(). Puedes recorrer un archivo comprimido sin crear una copia completa en disco.
import gzip
with gzip.open("eventos.jsonl.gz", "rt", encoding="utf-8") as archivo:
for linea in archivo:
procesar(linea)La descompresión consume CPU, pero ahorra espacio y operaciones de entrada y salida. Debes limitar tamaños cuando el archivo procede de una fuente no confiable.
Codificación y errores de texto
Especifica siempre una codificación. UTF-8 es la opción habitual, pero archivos antiguos pueden usar Windows-1252 o Latin-1. No utilices errors="ignore" automáticamente, porque puede eliminar información silenciosamente. Primero identifica el origen, prueba una muestra y registra cualquier sustitución.
Reanudar un proceso interrumpido
Para trabajos largos, guarda un punto de control: número de línea, desplazamiento de bytes o identificador del último registro confirmado. Al reiniciar, abre el archivo, avanza de forma controlada y continúa. El punto debe actualizarse después de guardar correctamente el resultado, no antes, para evitar pérdida de datos.
Escritura incremental
El resultado tampoco debe acumularse en una lista gigantesca. Escribe cada lote en CSV, base de datos o archivo temporal. Si la salida final es crítica, escribe en un nombre temporal, sincroniza, valida y reemplaza el destino al terminar. Así un fallo no deja un archivo aparentemente completo pero corrupto.
Evitar operaciones costosas dentro del bucle
Compila expresiones regulares antes de recorrer millones de líneas, prepara conexiones y constantes fuera del bucle y evita imprimir cada registro. El terminal puede convertirse en el cuello de botella. Para progreso, informa cada diez mil o cien mil elementos mediante logging.
Paralelismo con cuidado
Dividir un archivo por bytes puede cortar caracteres o registros. Para formatos basados en líneas, cada trabajador debe comenzar en un límite válido. Antes de usar procesos paralelos, mide una versión secuencial. Muchas tareas están limitadas por el disco y no mejoran al añadir trabajadores; incluso pueden empeorar por competencia de entrada y salida.
Pruebas recomendadas
Prueba un archivo vacío, una sola línea, caracteres Unicode, líneas enormes, registros inválidos, interrupciones y falta de espacio. Compara el total obtenido con un conjunto pequeño conocido. Mide memoria y tiempo, porque un programa correcto pero incapaz de terminar no es una solución completa.
Errores frecuentes
Los errores más comunes son usar read() sin límite, convertir iteradores a lista, acumular resultados, ignorar codificación, imprimir millones de veces y no cerrar archivos. También es peligroso confiar en el tamaño comprimido: un archivo pequeño puede expandirse de forma desproporcionada.
Conclusión
La clave para leer archivos gigantes es mantener un flujo constante y limitado. Itera línea por línea, usa bloques cuando sea necesario, crea generadores, procesa CSV por fragmentos y escribe resultados incrementalmente. Añade puntos de control, logs y pruebas con datos problemáticos. Con esta arquitectura, el tamaño del archivo deja de estar directamente limitado por la memoria RAM.





