Archivos

Intermedio
Python
Python
Actualizado: 29/08/2025

Lectura de archivos de texto

El manejo de archivos de texto constituye una habilidad fundamental en Python que permite a nuestros programas interactuar con datos persistentes. Los archivos de texto plano (.txt) representan la forma más directa de almacenar y recuperar información textual, desde configuraciones simples hasta registros de actividad.

Python ofrece una interfaz integrada para trabajar con archivos a través de la función open(), que nos permite acceder al contenido de archivos existentes en el sistema. La lectura de archivos sigue un patrón consistente: abrir el archivo, leer su contenido y cerrarlo adecuadamente.

Apertura básica de archivos

La función open() requiere como mínimo la ruta del archivo y devuelve un objeto archivo que podemos manipular:

# Apertura básica de un archivo
archivo = open('datos.txt', 'r')
contenido = archivo.read()
archivo.close()
print(contenido)

Sin embargo, esta aproximación presenta riesgos si ocurre un error antes de ejecutar close(). La forma recomendada es utilizar context managers con la declaración with:

# Forma segura de abrir archivos
with open('datos.txt', 'r') as archivo:
    contenido = archivo.read()
    print(contenido)
# El archivo se cierra automáticamente

Métodos de lectura

Python proporciona tres métodos principales para leer archivos de texto, cada uno adaptado a diferentes necesidades:

1 - El método read():

Lee todo el contenido del archivo como una cadena única. Es ideal para archivos pequeños que queremos procesar completamente:

# Leer un archivo de configuración completo
with open('config.txt', 'r') as archivo:
    configuracion = archivo.read()
    print(f"Configuración cargada:\n{configuracion}")

También podemos especificar un número de caracteres a leer:

# Leer solo los primeros 50 caracteres
with open('log.txt', 'r') as archivo:
    inicio = archivo.read(50)
    print(f"Primeros 50 caracteres: {inicio}")

2 - El método readline():

Lee una línea individual del archivo cada vez que se invoca. Incluye el carácter de nueva línea (\n) al final de cada línea:

# Leer línea por línea un archivo de log
with open('server.log', 'r') as archivo:
    primera_linea = archivo.readline()
    segunda_linea = archivo.readline()
    
    print(f"Primera entrada: {primera_linea.strip()}")
    print(f"Segunda entrada: {segunda_linea.strip()}")

Es especialmente útil para procesar archivos grandes línea por línea sin cargar todo el contenido en memoria:

# Buscar una línea específica en un log
with open('errores.log', 'r') as archivo:
    linea_actual = archivo.readline()
    numero_linea = 1
    
    while linea_actual:
        if 'ERROR' in linea_actual:
            print(f"Error encontrado en línea {numero_linea}: {linea_actual.strip()}")
            break
        linea_actual = archivo.readline()
        numero_linea += 1

3 - El método readlines():

Retorna una lista donde cada elemento es una línea del archivo. Cada línea conserva su carácter de nueva línea:

# Leer todas las líneas de una lista de tareas
with open('tareas.txt', 'r') as archivo:
    lineas = archivo.readlines()
    
    print(f"Tienes {len(lineas)} tareas pendientes:")
    for i, tarea in enumerate(lineas, 1):
        print(f"{i}. {tarea.strip()}")

Ejemplos prácticos

Procesamiento de un archivo de usuarios:

# Archivo: usuarios.txt
# Juan Pérez
# María García  
# Carlos López

with open('usuarios.txt', 'r') as archivo:
    usuarios = archivo.readlines()
    
    usuarios_limpios = []
    for usuario in usuarios:
        # Eliminar espacios y saltos de línea
        usuario_limpio = usuario.strip()
        if usuario_limpio:  # Ignorar líneas vacías
            usuarios_limpios.append(usuario_limpio)
    
    print(f"Usuarios registrados: {usuarios_limpios}")

Análisis de un archivo de log simple:

# Contar tipos de eventos en un log
with open('eventos.log', 'r') as archivo:
    contenido_completo = archivo.read()
    
    info_count = contenido_completo.count('[INFO]')
    warning_count = contenido_completo.count('[WARNING]')
    error_count = contenido_completo.count('[ERROR]')
    
    print(f"Resumen del log:")
    print(f"  Información: {info_count} eventos")
    print(f"  Advertencias: {warning_count} eventos") 
    print(f"  Errores: {error_count} eventos")

Iteración directa sobre archivos

Los objetos archivo en Python son iterables, lo que permite recorrer sus líneas de forma directa y eficiente:

# Forma pythónica de procesar líneas
with open('datos.txt', 'r') as archivo:
    for numero, linea in enumerate(archivo, 1):
        linea_limpia = linea.strip()
        if linea_limpia:
            print(f"Línea {numero}: {linea_limpia}")

Esta aproximación es más eficiente en memoria que readlines() para archivos grandes, ya que procesa una línea cada vez sin cargar todo el archivo.

Filtrado de contenido durante la lectura:

# Extraer solo líneas que contengan números de teléfono
with open('contactos.txt', 'r') as archivo:
    telefonos = []
    for linea in archivo:
        if linea.strip().startswith('+34') or linea.strip().startswith('6') or linea.strip().startswith('9'):
            telefonos.append(linea.strip())
    
    print(f"Teléfonos encontrados: {telefonos}")

La lectura de archivos forma la base para manipular datos externos en nuestros programas Python, permitiendo cargar configuraciones, procesar logs de aplicaciones y analizar datasets simples de forma eficiente y segura.

Escritura y modificación de archivos

La escritura de archivos permite que nuestros programas generen datos persistentes, desde crear registros de actividad hasta guardar configuraciones de usuario. Python maneja la escritura de archivos de forma similar a la lectura, pero requiere especificar el modo de apertura apropiado según nuestras necesidades.

Modos de apertura para escritura

Python ofrece varios modos de escritura que determinan cómo se comporta el archivo al abrirlo:

Modo 'w' (write): Crea un archivo nuevo o sobrescribe completamente un archivo existente:

# Crear un nuevo archivo de configuración
with open('config.txt', 'w') as archivo:
    archivo.write('debug=True\n')
    archivo.write('max_connections=100\n')
    archivo.write('timeout=30\n')

Modo 'a' (append): Añade contenido al final de un archivo existente, o crea uno nuevo si no existe:

# Agregar una entrada a un log existente
with open('actividad.log', 'a') as archivo:
    archivo.write('2025-01-15 10:30:22 - Usuario logueado\n')
    archivo.write('2025-01-15 10:30:45 - Datos cargados correctamente\n')

Modo 'r+' (read/write): Permite leer y escribir en un archivo existente sin truncarlo. La escritura comienza desde el inicio del archivo:

# Modificar un archivo existente manteniendo parte del contenido
with open('contador.txt', 'r+') as archivo:
    # Leer el valor actual
    contenido = archivo.read()
    contador_actual = int(contenido.strip()) if contenido.strip().isdigit() else 0
    
    # Volver al inicio del archivo
    archivo.seek(0)
    
    # Escribir el nuevo valor
    nuevo_contador = contador_actual + 1
    archivo.write(str(nuevo_contador))
    archivo.truncate()  # Eliminar contenido restante

Métodos de escritura

El método write(): Escribe una cadena en el archivo. No añade automáticamente saltos de línea:

# Crear un archivo de registro de errores
with open('errores.log', 'w') as archivo:
    archivo.write('[ERROR] 2025-01-15 14:22:33 - Conexión fallida\n')
    archivo.write('[ERROR] 2025-01-15 14:22:45 - Timeout en base de datos\n')
    archivo.write('[INFO] 2025-01-15 14:23:00 - Servicio reiniciado\n')

El método writelines(): Escribe una secuencia de cadenas al archivo. Tampoco añade saltos de línea automáticamente:

# Guardar una lista de tareas
tareas = [
    'Revisar correos electrónicos\n',
    'Actualizar documentación\n',
    'Preparar presentación\n',
    'Llamar al cliente\n'
]

with open('tareas_pendientes.txt', 'w') as archivo:
    archivo.writelines(tareas)

Ejemplos prácticos de escritura

Generación de un archivo de configuración:

# Crear configuración de una aplicación
configuracion = {
    'servidor': 'localhost',
    'puerto': 8080,
    'debug': True,
    'max_usuarios': 50
}

with open('app_config.txt', 'w') as archivo:
    archivo.write('# Configuración de la aplicación\n')
    archivo.write('# Generado automáticamente\n\n')
    
    for clave, valor in configuracion.items():
        archivo.write(f'{clave}={valor}\n')

Sistema de logging simple:

import datetime

def escribir_log(mensaje, nivel='INFO'):
    """Función para escribir entradas en un archivo de log"""
    timestamp = datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')
    entrada = f'[{nivel}] {timestamp} - {mensaje}\n'
    
    with open('aplicacion.log', 'a') as archivo:
        archivo.write(entrada)

# Uso del sistema de logging
escribir_log('Aplicación iniciada correctamente')
escribir_log('Error al conectar con la base de datos', 'ERROR')
escribir_log('Procesando 150 registros', 'INFO')

Modificación de archivos existentes

Actualizar contenido específico:

# Leer archivo, modificar contenido y reescribir
with open('usuarios.txt', 'r') as archivo:
    usuarios = archivo.readlines()

# Modificar la lista (añadir nuevo usuario)
usuarios.append('Pedro Martín\n')

# Reescribir el archivo con el contenido modificado
with open('usuarios.txt', 'w') as archivo:
    archivo.writelines(usuarios)

Reemplazar texto en un archivo:

# Cambiar una configuración específica
with open('config.txt', 'r') as archivo:
    contenido = archivo.read()

# Reemplazar valor
contenido_modificado = contenido.replace('debug=False', 'debug=True')

with open('config.txt', 'w') as archivo:
    archivo.write(contenido_modificado)

Combinación de lectura y escritura

Procesamiento y filtrado de datos:

# Leer un log y crear un resumen de errores
with open('servidor.log', 'r') as log_completo:
    lineas = log_completo.readlines()

# Filtrar solo errores y warnings
eventos_importantes = []
for linea in lineas:
    if '[ERROR]' in linea or '[WARNING]' in linea:
        eventos_importantes.append(linea)

# Crear archivo con eventos importantes
with open('eventos_criticos.log', 'w') as resumen:
    resumen.write('# Resumen de eventos críticos\n')
    resumen.write(f'# Total de eventos: {len(eventos_importantes)}\n\n')
    resumen.writelines(eventos_importantes)

Contador de visitas persistente:

def actualizar_contador_visitas():
    """Incrementa y guarda el contador de visitas"""
    try:
        # Intentar leer el contador actual
        with open('visitas.txt', 'r') as archivo:
            visitas = int(archivo.read().strip())
    except FileNotFoundError:
        # Si el archivo no existe, empezar desde 0
        visitas = 0
    
    # Incrementar contador
    visitas += 1
    
    # Guardar nuevo valor
    with open('visitas.txt', 'w') as archivo:
        archivo.write(str(visitas))
    
    return visitas

# Simular varias visitas
print(f"Visita número: {actualizar_contador_visitas()}")
print(f"Visita número: {actualizar_contador_visitas()}")
print(f"Visita número: {actualizar_contador_visitas()}")

Escritura con formato

Generación de reportes estructurados:

# Crear un reporte de ventas
ventas = [
    {'producto': 'Laptop', 'cantidad': 15, 'precio': 899.99},
    {'producto': 'Mouse', 'cantidad': 45, 'precio': 25.50},
    {'producto': 'Teclado', 'cantidad': 30, 'precio': 75.00}
]

with open('reporte_ventas.txt', 'w') as reporte:
    reporte.write('REPORTE DE VENTAS\n')
    reporte.write('=' * 50 + '\n\n')
    
    total_general = 0
    for venta in ventas:
        subtotal = venta['cantidad'] * venta['precio']
        total_general += subtotal
        
        linea = f"{venta['producto']:<15} | {venta['cantidad']:>3} unidades | €{subtotal:>8.2f}\n"
        reporte.write(linea)
    
    reporte.write('-' * 50 + '\n')
    reporte.write(f"{'TOTAL GENERAL':<15} | {'':>3}           | €{total_general:>8.2f}\n")

Backup de datos importantes:

import datetime

def crear_backup_configuracion():
    """Crea una copia de seguridad de la configuración actual"""
    timestamp = datetime.datetime.now().strftime('%Y%m%d_%H%M%S')
    archivo_backup = f'config_backup_{timestamp}.txt'
    
    try:
        # Leer configuración actual
        with open('config.txt', 'r') as original:
            contenido = original.read()
        
        # Crear backup
        with open(archivo_backup, 'w') as backup:
            backup.write(f'# Backup creado: {datetime.datetime.now()}\n')
            backup.write(contenido)
        
        print(f"Backup creado: {archivo_backup}")
        
    except FileNotFoundError:
        print("No se encontró el archivo de configuración original")

crear_backup_configuracion()

La escritura de archivos nos permite crear aplicaciones que mantengan estado, generen reportes y registren actividad de forma permanente. El uso consistente de context managers garantiza que los archivos se cierren correctamente, evitando la pérdida de datos y problemas de acceso concurrente.

Context managers y manejo de errores

Los context managers representan uno de los mecanismos más elegantes de Python para gestionar recursos que requieren inicialización y limpieza. Al trabajar con archivos, garantizan que estos se cierren correctamente independientemente de si ocurren errores durante el procesamiento.

Un context manager implementa el protocolo de contexto a través de los métodos especiales __enter__ y __exit__. La declaración with invoca automáticamente estos métodos, asegurando que los recursos se liberen apropiadamente.

Funcionamiento interno de context managers

Cuando utilizamos with open(), Python ejecuta una secuencia específica de operaciones:

# Lo que ocurre internamente con with open()
with open('datos.txt', 'r') as archivo:
    contenido = archivo.read()
    print(contenido)
# Equivale a:
# archivo = open('datos.txt', 'r').__enter__()
# try:
#     contenido = archivo.read()
#     print(contenido)
# finally:
#     archivo.__exit__(None, None, None)

Esta gestión automática es crucial porque garantiza el cierre del archivo incluso si ocurre una excepción:

# El archivo se cierra automáticamente incluso si hay error
try:
    with open('datos.txt', 'r') as archivo:
        contenido = archivo.read()
        resultado = 10 / 0  # Esto genera ZeroDivisionError
        print(resultado)
except ZeroDivisionError:
    print("Error de división por cero")
# El archivo ya está cerrado aquí

Manejo de errores específicos de archivos

Los errores más comunes al trabajar con archivos requieren estrategias específicas de manejo:

FileNotFoundError: Se produce cuando intentamos acceder a un archivo inexistente:

def leer_configuracion(nombre_archivo):
    """Lee configuración con manejo de archivo faltante"""
    try:
        with open(nombre_archivo, 'r') as archivo:
            return archivo.read()
    except FileNotFoundError:
        print(f"El archivo {nombre_archivo} no existe")
        # Crear archivo con configuración por defecto
        with open(nombre_archivo, 'w') as archivo:
            configuracion_defecto = "debug=False\nversion=1.0\n"
            archivo.write(configuracion_defecto)
            return configuracion_defecto

# Uso robusto
config = leer_configuracion('app.conf')
print("Configuración cargada:", config)

PermissionError: Ocurre cuando no tenemos permisos suficientes para acceder al archivo:

def escribir_log_seguro(mensaje):
    """Escribe log manejando errores de permisos"""
    archivos_log = ['app.log', 'backup.log', 'temp.log']
    
    for archivo_log in archivos_log:
        try:
            with open(archivo_log, 'a') as archivo:
                archivo.write(f"{mensaje}\n")
                print(f"Log escrito en {archivo_log}")
                return True
        except PermissionError:
            print(f"Sin permisos para escribir en {archivo_log}")
            continue
        except Exception as e:
            print(f"Error inesperado con {archivo_log}: {e}")
            continue
    
    print("No se pudo escribir el log en ningún archivo")
    return False

# Intento de escritura con fallback
escribir_log_seguro("Aplicación iniciada correctamente")

IsADirectoryError: Se produce cuando intentamos abrir un directorio como si fuera un archivo:

def validar_y_leer_archivo(ruta):
    """Valida que la ruta sea un archivo antes de leerlo"""
    import os
    
    if not os.path.exists(ruta):
        raise FileNotFoundError(f"La ruta {ruta} no existe")
    
    if os.path.isdir(ruta):
        raise IsADirectoryError(f"La ruta {ruta} es un directorio, no un archivo")
    
    try:
        with open(ruta, 'r') as archivo:
            return archivo.read()
    except IsADirectoryError:
        print(f"Error: {ruta} es un directorio")
        return None
    except PermissionError:
        print(f"Error: Sin permisos para leer {ruta}")
        return None

# Uso con validación
contenido = validar_y_leer_archivo('documentos')  # Si es directorio
contenido = validar_y_leer_archivo('documento.txt')  # Si es archivo

Manejo múltiple de archivos

Los context managers permiten gestionar varios archivos simultáneamente de forma elegante:

def copiar_archivo_con_backup(origen, destino):
    """Copia un archivo creando backup del destino si existe"""
    try:
        # Abrir múltiples archivos en un solo context manager
        with open(origen, 'r') as archivo_origen, \
             open(destino, 'w') as archivo_destino:
            
            contenido = archivo_origen.read()
            archivo_destino.write(contenido)
            print(f"Archivo copiado de {origen} a {destino}")
            
    except FileNotFoundError as e:
        print(f"Error: Archivo no encontrado - {e}")
    except PermissionError as e:
        print(f"Error: Permisos insuficientes - {e}")
    except Exception as e:
        print(f"Error inesperado: {e}")

# Uso seguro de copia
copiar_archivo_con_backup('datos.txt', 'datos_copia.txt')

Procesamiento de múltiples archivos con manejo individual de errores:

def procesar_lote_archivos(lista_archivos):
    """Procesa múltiples archivos manejando errores individualmente"""
    resultados = {
        'exitosos': [],
        'fallidos': [],
        'total_lineas': 0
    }
    
    for archivo in lista_archivos:
        try:
            with open(archivo, 'r') as f:
                lineas = f.readlines()
                resultados['exitosos'].append(archivo)
                resultados['total_lineas'] += len(lineas)
                print(f"✓ {archivo}: {len(lineas)} líneas procesadas")
                
        except FileNotFoundError:
            resultados['fallidos'].append(f"{archivo} (no encontrado)")
            print(f"✗ {archivo}: archivo no encontrado")
            
        except PermissionError:
            resultados['fallidos'].append(f"{archivo} (sin permisos)")
            print(f"✗ {archivo}: sin permisos de lectura")
            
        except UnicodeDecodeError:
            resultados['fallidos'].append(f"{archivo} (encoding inválido)")
            print(f"✗ {archivo}: problema de codificación")
    
    return resultados

# Procesar lote de archivos
archivos = ['log1.txt', 'log2.txt', 'config.txt', 'inexistente.txt']
resultado = procesar_lote_archivos(archivos)
print(f"\nResumen: {len(resultado['exitosos'])} exitosos, {len(resultado['fallidos'])} fallidos")

Context managers personalizados para archivos

Podemos crear context managers especializados para casos específicos:

class GestorArchivoSeguro:
    """Context manager personalizado con logging de operaciones"""
    
    def __init__(self, nombre_archivo, modo='r'):
        self.nombre_archivo = nombre_archivo
        self.modo = modo
        self.archivo = None
    
    def __enter__(self):
        try:
            self.archivo = open(self.nombre_archivo, self.modo)
            print(f"📂 Archivo {self.nombre_archivo} abierto en modo {self.modo}")
            return self.archivo
        except Exception as e:
            print(f"❌ Error al abrir {self.nombre_archivo}: {e}")
            raise
    
    def __exit__(self, tipo_excepcion, valor_excepcion, traceback):
        if self.archivo:
            self.archivo.close()
            print(f"📂 Archivo {self.nombre_archivo} cerrado correctamente")
        
        if tipo_excepcion:
            print(f"⚠️  Excepción durante procesamiento: {valor_excepcion}")
        
        # No suprimir la excepción (return None implícito)

# Uso del context manager personalizado
try:
    with GestorArchivoSeguro('datos.txt', 'r') as archivo:
        contenido = archivo.read()
        print(f"Contenido leído: {len(contenido)} caracteres")
except FileNotFoundError:
    print("Archivo no encontrado, creando uno nuevo...")
    with GestorArchivoSeguro('datos.txt', 'w') as archivo:
        archivo.write("Archivo creado automáticamente\n")

Estrategias avanzadas de manejo de errores

Reintentos automáticos con backoff:

import time

def leer_archivo_con_reintentos(nombre_archivo, max_intentos=3):
    """Lee archivo con reintentos en caso de errores temporales"""
    for intento in range(1, max_intentos + 1):
        try:
            with open(nombre_archivo, 'r') as archivo:
                contenido = archivo.read()
                print(f"✓ Archivo leído exitosamente en intento {intento}")
                return contenido
                
        except PermissionError:
            print(f"⚠️  Intento {intento}: Sin permisos, reintentando en {intento} segundos...")
            if intento < max_intentos:
                time.sleep(intento)
            else:
                print("❌ Agotados los reintentos por permisos")
                raise
                
        except FileNotFoundError:
            print(f"❌ Archivo {nombre_archivo} no existe")
            raise  # No reintentar para archivos inexistentes
            
        except Exception as e:
            print(f"⚠️  Intento {intento}: Error inesperado {e}")
            if intento < max_intentos:
                time.sleep(intento)
            else:
                print("❌ Agotados los reintentos")
                raise

# Uso con reintentos
try:
    contenido = leer_archivo_con_reintentos('archivo_temporal.txt')
    print("Archivo procesado correctamente")
except Exception as e:
    print(f"Error final: {e}")

Validación y limpieza automática:

def procesar_archivo_robusto(nombre_archivo):
    """Procesamiento robusto con validación y limpieza"""
    archivo_temporal = None
    
    try:
        # Validar que el archivo existe y es legible
        with open(nombre_archivo, 'r') as archivo:
            primer_linea = archivo.readline()
            if not primer_linea.strip():
                raise ValueError("El archivo está vacío")
        
        # Crear archivo temporal para procesamiento
        archivo_temporal = f"{nombre_archivo}.temp"
        
        with open(nombre_archivo, 'r') as origen, \
             open(archivo_temporal, 'w') as temporal:
            
            lineas_procesadas = 0
            for numero, linea in enumerate(origen, 1):
                linea_limpia = linea.strip()
                if linea_limpia and not linea_limpia.startswith('#'):
                    temporal.write(f"{linea_limpia}\n")
                    lineas_procesadas += 1
        
        print(f"✓ Procesadas {lineas_procesadas} líneas válidas")
        return archivo_temporal
        
    except FileNotFoundError:
        print(f"❌ El archivo {nombre_archivo} no existe")
        return None
        
    except ValueError as e:
        print(f"❌ Error de validación: {e}")
        return None
        
    except Exception as e:
        print(f"❌ Error inesperado: {e}")
        # Limpiar archivo temporal si existe
        if archivo_temporal:
            try:
                import os
                os.remove(archivo_temporal)
                print("🧹 Archivo temporal limpiado")
            except:
                pass
        return None

# Procesamiento con limpieza automática
resultado = procesar_archivo_robusto('datos_entrada.txt')
if resultado:
    print(f"Archivo procesado guardado en: {resultado}")

El uso consistente de context managers junto con estrategias apropiadas de manejo de errores crea aplicaciones robustas que pueden recuperarse de fallos comunes y mantener la integridad de los datos. Esta aproximación profesional es esencial para aplicaciones que manejan archivos en entornos de producción.

Fuentes y referencias

Documentación oficial y recursos externos para profundizar en Python

Documentación oficial de Python
Alan Sastre - Autor del tutorial

Alan Sastre

Ingeniero de Software y formador, CEO en CertiDevs

Ingeniero de software especializado en Full Stack y en Inteligencia Artificial. Como CEO de CertiDevs, Python es una de sus áreas de expertise. Con más de 15 años programando, 6K seguidores en LinkedIn y experiencia como formador, Alan se dedica a crear contenido educativo de calidad para desarrolladores de todos los niveles.

Más tutoriales de Python

Explora más contenido relacionado con Python y continúa aprendiendo con nuestros tutoriales gratuitos.

Aprendizajes de esta lección

  • Comprender cómo abrir, leer y cerrar archivos de texto en Python.
  • Aprender los diferentes métodos de lectura: read(), readline() y readlines().
  • Conocer los modos de apertura para escritura y cómo modificar archivos existentes.
  • Entender el uso de context managers para gestionar archivos de forma segura.
  • Aplicar técnicas de manejo de errores comunes al trabajar con archivos.