Manejo de errores con archivos: bloque try-except
Introducción
Cuando trabajamos con archivos en Python, numerosas situaciones pueden generar errores: el archivo puede no existir, podemos no tener permisos suficientes, el disco puede estar lleno o el archivo podría estar bloqueado por otro proceso. Sin un manejo adecuado de estas situaciones, nuestros programas podrían terminar abruptamente o comportarse de manera inesperada. En este artículo, aprenderemos a utilizar el bloque try-except
para manejar estos errores de forma elegante, lo que nos permitirá crear aplicaciones más robustas y profesionales que puedan recuperarse de situaciones problemáticas al trabajar con archivos.
Errores comunes al trabajar con archivos
Antes de ver cómo manejar los errores, vamos a entender qué tipos de errores pueden surgir:
Errores de acceso a archivos
# Intentar abrir un archivo que no existe
try:
archivo = open("archivo_inexistente.txt", "r")
contenido = archivo.read()
archivo.close()
except FileNotFoundError:
print("Error: El archivo no existe.")
Errores de permisos
# Intentar escribir en un archivo sin permisos
try:
archivo = open("/etc/sistema_config.txt", "w")
archivo.write("Nueva configuración")
archivo.close()
except PermissionError:
print("Error: No tienes permisos para escribir en este archivo.")
Errores de sintaxis en la ruta
# Errores en la especificación de la ruta
try:
archivo = open("C:\\archivos\\:archivo*.txt", "r") # Caracteres no válidos en Windows
contenido = archivo.read()
archivo.close()
except OSError as error:
print(f"Error del sistema: {error}")
Errores de E/S (entrada/salida)
# Errores de E/S (como disco lleno, desconexión de red, etc.)
try:
archivo = open("//servidor/compartido/datos.txt", "r")
contenido = archivo.read()
archivo.close()
except IOError as error:
print(f"Error de E/S: {error}")
Estructura del bloque try-except
La estructura básica para manejar errores en Python es:
try:
# Código que puede generar un error
pass
except TipoDeError:
# Código que se ejecuta si ocurre ese error
pass
else:
# Código que se ejecuta si no hay errores
pass
finally:
# Código que se ejecuta siempre, haya error o no
pass
Aplicando la estructura a archivos
try:
# Intentar abrir y leer un archivo
archivo = open("datos.txt", "r")
contenido = archivo.read()
print(contenido)
except FileNotFoundError:
# Se ejecuta si el archivo no existe
print("Error: El archivo no se encontró")
except PermissionError:
# Se ejecuta si no hay permisos suficientes
print("Error: No tienes permisos para leer este archivo")
else:
# Se ejecuta si todo va bien (opcional)
print("Archivo leído correctamente")
finally:
# Se ejecuta siempre, haya error o no (opcional)
# Asegurarse de que el archivo se cierre si se abrió
if 'archivo' in locals() and not archivo.closed:
archivo.close()
print("Archivo cerrado")
Manejo específico de excepciones para archivos
Capturar múltiples excepciones
Podemos capturar varios tipos de excepciones de forma separada:
try:
archivo = open("config.txt", "r")
configuracion = archivo.read()
valores = configuracion.split("=")
resultado = 100 / int(valores[1])
archivo.close()
except FileNotFoundError:
print("El archivo de configuración no existe.")
except IndexError:
print("El formato del archivo de configuración es incorrecto.")
except ZeroDivisionError:
print("El valor de configuración no puede ser cero.")
except ValueError:
print("El valor de configuración debe ser un número.")
Capturar múltiples excepciones en una línea
También podemos agrupar excepciones:
try:
archivo = open("datos.txt", "r")
contenido = archivo.read()
archivo.close()
except (FileNotFoundError, PermissionError) as error:
print(f"Error al acceder al archivo: {error}")
Capturar cualquier excepción
Aunque no es una buena práctica, a veces necesitamos capturar cualquier error:
try:
archivo = open("datos.txt", "r")
contenido = archivo.read()
archivo.close()
except Exception as error:
print(f"Ocurrió un error inesperado: {error}")
# Aquí podríamos registrar el error en un log
Patrón de uso con el gestor de contexto (with)
El patrón recomendado para trabajar con archivos es usar el gestor de contexto with
, que se encarga automáticamente de cerrar el archivo:
try:
with open("datos.txt", "r") as archivo:
contenido = archivo.read()
# El archivo se cierra automáticamente al salir del bloque with
except FileNotFoundError:
print("El archivo no existe")
Ventajas del gestor de contexto
- Cierra el archivo automáticamente, incluso si ocurre una excepción
- Código más limpio y menos propenso a errores
- Manejo de recursos más eficiente
Mejores prácticas para el manejo de errores con archivos
1. Utilizar siempre el gestor de contexto (with)
try:
with open("datos.txt", "r") as archivo:
contenido = archivo.read()
except FileNotFoundError:
print("El archivo no existe")
2. Ser específico con las excepciones
# Malo: capturar todas las excepciones
try:
with open("datos.txt", "r") as archivo:
contenido = archivo.read()
except Exception:
print("Ocurrió un error") # Poco informativo
# Bueno: capturar excepciones específicas
try:
with open("datos.txt", "r") as archivo:
contenido = archivo.read()
except FileNotFoundError:
print("El archivo no existe")
except PermissionError:
print("No tienes permisos para acceder al archivo")
3. Proporcionar información útil sobre el error
try:
ruta_archivo = "C:/usuarios/datos.txt"
with open(ruta_archivo, "r") as archivo:
contenido = archivo.read()
except FileNotFoundError:
print(f"El archivo {ruta_archivo} no existe. Verifica la ruta.")
except PermissionError:
print(f"No tienes permisos para acceder a {ruta_archivo}.")
except Exception as error:
print(f"Error inesperado al leer {ruta_archivo}: {error}")
4. Registrar errores para depuración
import logging
# Configurar el sistema de logging
logging.basicConfig(
filename='errores_app.log',
level=logging.ERROR,
format='%(asctime)s - %(levelname)s - %(message)s'
)
try:
with open("datos_importantes.txt", "r") as archivo:
contenido = archivo.read()
except Exception as error:
# Mostrar mensaje al usuario
print("No se pudo acceder al archivo de datos")
# Registrar detalles técnicos en el log
logging.error(f"Error al leer datos_importantes.txt: {error}", exc_info=True)
Ejemplos prácticos
Ejemplo 1: Lector de configuración robusto
def leer_configuracion(ruta_archivo):
"""Lee un archivo de configuración en formato clave=valor."""
config = {}
try:
with open(ruta_archivo, "r") as archivo:
for linea in archivo:
linea = linea.strip()
# Ignorar líneas vacías o comentarios
if not linea or linea.startswith("#"):
continue
try:
clave, valor = linea.split("=", 1)
config[clave.strip()] = valor.strip()
except ValueError:
print(f"Advertencia: Formato incorrecto en línea: {linea}")
return config
except FileNotFoundError:
print(f"El archivo de configuración {ruta_archivo} no existe.")
return {}
except PermissionError:
print(f"No tienes permisos para leer {ruta_archivo}.")
return {}
except Exception as error:
print(f"Error al leer la configuración: {error}")
return {}
# Uso
configuracion = leer_configuracion("config.txt")
print("Configuración cargada:", configuracion)
Ejemplo 2: Copia de seguridad de archivos
import shutil
from pathlib import Path
import datetime
def hacer_backup(ruta_archivo):
"""Crea una copia de seguridad de un archivo con fecha y hora."""
ruta = Path(ruta_archivo)
# Verificar que el archivo existe
if not ruta.exists():
print(f"Error: El archivo {ruta.name} no existe.")
return False
# Crear nombre para la copia de seguridad
fecha_hora = datetime.datetime.now().strftime("%Y%m%d_%H%M%S")
nombre_backup = f"{ruta.stem}_{fecha_hora}{ruta.suffix}"
ruta_backup = ruta.parent / "backups" / nombre_backup
try:
# Asegurar que existe el directorio de backups
carpeta_backup = ruta.parent / "backups"
carpeta_backup.mkdir(exist_ok=True)
# Copiar el archivo
shutil.copy2(ruta, ruta_backup)
print(f"Backup creado: {ruta_backup}")
return True
except PermissionError:
print(f"Error: No tienes permisos para crear el backup.")
return False
except shutil.SameFileError:
print(f"Error: El archivo de origen y destino son el mismo.")
return False
except OSError as error:
print(f"Error del sistema: {error}")
return False
# Uso
# hacer_backup("documento_importante.txt")
Ejemplo 3: Verificador de archivos
from pathlib import Path
def verificar_archivos(lista_archivos):
"""Verifica que los archivos existan y sean accesibles."""
resultados = []
for ruta in lista_archivos:
archivo = Path(ruta)
estado = {
"ruta": str(archivo),
"nombre": archivo.name,
"existe": False,
"es_archivo": False,
"tamaño": 0,
"permisos": {
"lectura": False,
"escritura": False
},
"error": None
}
try:
# Verificar existencia
if archivo.exists():
estado["existe"] = True
# Verificar si es archivo
if archivo.is_file():
estado["es_archivo"] = True
# Obtener tamaño
estado["tamaño"] = archivo.stat().st_size
# Verificar permisos
try:
# Prueba de lectura
with open(archivo, "r") as f:
f.read(1) # Leer solo un byte para verificar
estado["permisos"]["lectura"] = True
except PermissionError:
pass
try:
# Prueba de escritura (sin modificar el archivo)
with open(archivo, "a") as f:
pass
estado["permisos"]["escritura"] = True
except PermissionError:
pass
except Exception as error:
estado["error"] = str(error)
resultados.append(estado)
return resultados
# Uso
# archivos = ["datos1.txt", "datos2.txt", "carpeta/datos3.txt"]
# resultados = verificar_archivos(archivos)
# for resultado in resultados:
# print(f"Archivo: {resultado['nombre']}")
# print(f" Existe: {resultado['existe']}")
# if resultado['existe'] and resultado['es_archivo']:
# print(f" Tamaño: {resultado['tamaño']} bytes")
# print(f" Permiso lectura: {resultado['permisos']['lectura']}")
# print(f" Permiso escritura: {resultado['permisos']['escritura']}")
# if resultado['error']:
# print(f" Error: {resultado['error']}")
Resumen
El manejo adecuado de errores al trabajar con archivos es esencial para desarrollar aplicaciones robustas. En este artículo, hemos aprendido a utilizar el bloque try-except
para capturar y manejar distintos tipos de excepciones que pueden surgir al manipular archivos. Hemos visto cómo proporcionar información útil al usuario, registrar errores para depuración y seguir buenas prácticas como el uso del gestor de contexto with
. También hemos explorado ejemplos prácticos que demuestran cómo implementar un manejo de errores profesional en situaciones cotidianas.
En el siguiente artículo, aprenderemos a trabajar con archivos CSV, un formato ampliamente utilizado para el intercambio de datos tabulares, y veremos cómo aplicar los conocimientos adquiridos hasta ahora para procesar este tipo de archivos de manera eficiente.