Ficheros de texto en Python

Es muy habitual trabajar con ficheros de texto en Python. Los utilizaremos como sistema de persistencia de datos, para guardar datos y para recuperarlos luego.

En este post vamos a ver cómo realizar las funciones básicas con ficheros de texto: leer, escribir y añadir datos.

Abrir un fichero de texto

Lo primero que tenemos que hacer para trabajar con un fichero de texto es abrirlo y cargar sus datos. La función open() abre un archivo y devuelve un objeto para leer y escribir en el fichero.

La sintaxis básica de la función es la siguiente:

fichero=open(ruta_fichero, modo_apertura, encoding) 

El parámetro modo_apertura presenta los siguientes valores posibles:

               ‘r’: Solo lectura (valor por defecto)

               ‘w’: Solo escritura (en caso de que ya exista, lo borra).

               ‘a’: Appending. Escritura al final del fichero.

               ‘r+’: Para lectura y escritura

Te recomiendo que en el parámetro encoding lo pases con el valor «utf-8». Con esa codificación te aseguras que lees bien las eñes y las tildes.

fichero = open("Suppliers.txt",'r',encoding="utf-8") 
	for line in f: 
			print(line) 
f.close()

En el código de arriba, abrimos el fichero en modo lectura, lo leemos con un bucle for línea a línea, lo imprimimos y cuando hemos acabado lo cerramos

Después de cargar los datos del fichero, tenemos que cerrarlo con el metodo close()

El modo recomendado para abrir un fichero de texto es usar with open(). La ventaja de este método es que no tenemos que preocuparnos de cerrar el fichero, la sentencia with lo hace cuando acaba de trabajar con él.

with open("Suppliers.txt") as file_object:
    contenido=file_object.read()
print (contenido)

Hay que tener en cuenta que dejar un fichero sin cerrar después de trabajar con él puede ocasionar problemas serios, pudiéndose llegar a perder los datos almacenados en el mismo.

Leer un fichero de texto

Una vez hemos abierto un fichero de texto y tenemos su información en un objeto, podemos leer los datos de diversas maneras. Por ejemplo: carácter a carácter:

with open("Suppliers.txt") as file_object:
    contenido=file_object.read()
for caracter in contenido:
    print(carácter,end=””)

En el código anterior, con el método read() cargamos todo el texto del fichero en contenido y luego lo recorremos caracter a caracter con el bucle for. Aquí, al usar with open() no hay que preocuparse de cerrar el fichero, Python lo hace automaticamente cuando acaba de cargar los datos en contenido.

También podemos recorrer el fichero línea a línea con un bucle for:

with open("Suppliers.txt") as file_object:
    for linea in file_object:
        print(linea)

En este caso, permanecemos con el fichero abierto trabajando línea a línea, pintandolas por pantalla. Si el tamaño del fichero es muy grande es posible que nos veamos forzados a trabajar de este modo, ya que no tendríamos la posibilidad de guardar todo el fichero en una variable como el caso anterior, cuando leiamos caracter a caracter.

También podemos leer las líneas de un fichero directamente con el método readlines() y crearnos una lista con las líneas. Si el fichero es muy grande, podría causar problemas al intentar guardar todos los datos en una lista.

with open("Suppliers.txt") as file_object:
    lineas = file_object.readlines()
for linea in lineas:
    print(linea)

Escribir en un fichero de texto

Para escribir en un fichero de texto nuevo, usamos with open y el método write. Un ejemplo sería:

fichero="Planetas.txt"
with open(fichero,"w") as objetoFichero:
    objetoFichero.write("Jupiter\n")
    objetoFichero.write("Neptuno\n")
    objetoFichero.write("Saturno\n")

De nuevo, aquí tenemos la ventaja de que con with open no hay que preocuparse de cerrar el fichero, Python lo hace automáticamente cuando acaba de trabajar con él.

Si lo que queremos es añadir datos a un fichero ya existente, sencillamente tendríamos que abrirlo con el método de apertura: «a»

fichero="Planetas.txt"
with open(fichero,"a") as objetoFichero:
    objetoFichero.write("Mercurio\n")
    objetoFichero.write("Venus\n")

NOTA:

Este post es parte de la colección “Python”. Puedes ver el índice de esta colección aquí.

Excepciones en Python

Para manejar los errores de entorno, que no dependen del programador, sino que los genera el usuario, vamos a emplear las excepciones. Las excepciones en Python son eventos que interrumpen el flujo normal de un programa cuando ocurre un error (por ejemplo, dividir entre cero o intentar acceder a un fichero que no existe o que no está localizado en la carpeta en la que lo estamos buscando). En lugar de dejar que el programa falle sin control, Python permite capturar y manejar estas situaciones con bloques try-except, y complementar el control con else y finally para ejecutar código cuando no hay errores o siempre, respectivamente. También es posible lanzar errores de forma explícita con raise y definir excepciones propias para representar problemas específicos de tu aplicación.

Bloque try-except

En el bloque try pondremos las instrucciones que pueden generar la excepción y en el bloque except las instrucciones para gestionar dicha excepción en el caso que se produzca.

Ejemplo:

fichero="Planetas.txt"
try:
    with open(fichero) as objetoFichero:
        contenido=objetoFichero.read()
except FileNotFoundError:
    print("Ufff, parece que el fichero ",fichero," no existe")

print(contenido)

Se pueden capturar distintos tipos de excepciones con múltiples except. Hay que colocar primero las excepciones más específicas y al final las más genéricas, ya que las clausulas except se evalúan en orden, y cuando coincide se ejecuta y no se evalúan más clausula except.

fichero = "planetas.txt"
try:
    with open(fichero) as fh:
        contenido = fh.read()
except FileNotFoundError as e:
    print(f"No se encuentra el fichero: {e.filename}")
except PermissionError:
    print("No tienes permisos para abrir el fichero.")
except OSError as e:
    print(f"Otro error de E/S: {e}")
except Exception as e:
    print(f"Error inesperado: {type(e).__name__}: {e}")
else:
    print(contenido)
finally:
    print("Este bloque se ejecuta SIEMPRE (para limpieza, cerrar recursos, etc.).")

La última clausula except, es una captura genérica, que recogería cualquier otra excepción que no se halla chequeado antes.

Clausula else

Más correcto sería emplear el bloque else para que se ejecuten las instrucciones en el caso de que no se produzca ninguna excepción:

fichero="Planetas.txt"
try:
    with open(fichero) as objetoFichero:
        contenido=objetoFichero.read()
except FileNotFoundError:
    print("Ufff, parece que el fichero ",fichero," no existe")
else:
    print(contenido)

Clausula finally

Las instrucciones en el bloque finally siempre se ejecutan, haya o no excepción, incluso si hay return en try/except. Conviene usarla cuando tengamos que garantizar alguna operación de limpieza, halla o no éxito. Por ejemplo, cerrar un fichero o cerrar una conexión a una base de datos.

Ejemplo:

f = open("data.txt")
try:
    data = f.read()
except FileNotFoundError:
    print("Archivo no encontrado")
finally:
    f.close()

Encadenamiento(raise, raise from)

El encadenamiento y relanzamiento de excepciones en Python permite propagar errores con contexto. Se puede atrapar una excepción de bajo nivel, añadir información útil y volver a lanzarla. Con raise se relanza la excepción actual o una nueva, y con raise from se crea un encadenamiento explícito que preserva la causa original, lo que facilita el diagnóstico de errores.

def cargar_config(ruta):
    try:
        import json
        with open(ruta, "r", encoding="utf-8") as fh:
            return json.load(fh)
    except FileNotFoundError as e:
        raise RuntimeError("No se pudo cargar la configuración") from e
try:
    cargar_config("config.json")
except RuntimeError as e:
    print(f"Fallo: {e} (causa: {e.__cause__})")

Excepciones frecuentes

Las excepciones más habituales que podemos encontrarnos son:

Excepción¿Cuándo ocurre?Ejemplo
ValueErrorValor con tipo correcto pero contenido inválidoint(‘abc’)
TypeErrorOperación no soporta por el tipo de objeto al que se aplicada‘a’ + 5
KeyErrorClave inexistente en diccionariod[‘x’]
IndexErrorÍndice fuera de rango[1,2][7]
FileNotFoundErrorFichero no se encuentra en la ruta indicadaopen(‘data.txt’)
PermissionErrorSin permisos de accesoopen(‘/root/privado’)
ZeroDivisionErrorDivisión por cero7/0
OSErrorErrores de sistema/IOos.remove(‘bloqueado’)
KeyboardInterruptInterrupción manual (Ctrl+C)
Tabla de excepciones más comunes

NOTA:

Este post es parte de la colección “Python”. Puedes ver el índice de esta colección aquí.

Ejercicios con Clases y Objetos en Python

Aquí dejo una propuesta de ejercicios con Clases y Objetos en python, para practicar lo aprendido en el post de Clases y Objetos.

Ejercicio 1: Clase Dispositivos e hijas

Define una clase Dispositivo con los atributos: marca, modelo y precio. Luego, crea las clases hijas: Smartphone, Tablet y Portatil, que hereden de Dispositivo y añade al menos un atributo específico a cada una de ellas.

Crea un objeto de cada clase hija (3 objetos en total) y pinta por pantalla todos los parámetros de uno de ellos.

Solución propuesta:

class Dispositivo:
    def __init__(self, marca, modelo, precio):
        self.marca = marca
        self.modelo = modelo
        self.precio = precio

class Smartphone(Dispositivo):
    def __init__(self, marca, modelo, precio, sistema_operativo):
        super().__init__(marca, modelo, precio)
        self.sistema_operativo = sistema_operativo

class Tablet(Dispositivo):
    def __init__(self, marca, modelo, precio, tamano_pantalla):
        super().__init__(marca, modelo, precio)
        self.tamano_pantalla = tamano_pantalla

class Portatil(Dispositivo):
    def __init__(self, marca, modelo, precio, capacidad_almacenamiento):
        super().__init__(marca, modelo, precio)
        self.capacidad_almacenamiento = capacidad_almacenamiento

# Crear objetos de cada clase hija
smartphone = Smartphone("Samsung", "Galaxy S20", 999, "Android")
tablet = Tablet("Apple", "iPad Pro", 799, "12.9 pulgadas")
portatil = Portatil("Dell", "XPS 13", 1299, "512 GB SSD")

# Imprimir todos los parámetros de uno de los objetos
print("Marca:", smartphone.marca)
print("Modelo:", smartphone.modelo)
print("Precio:", smartphone.precio)
print("Sistema Operativo:", smartphone.sistema_operativo)

Ejercicio 2: Clase Rectángulo y Clase Punto

Crea una clase llamada «Punto» que tenga los atributos «x» e «y». Crea otra clase llamada «Rectangulo», que tiene como parámetros dos objetos de tipo Punto.

La clase Rectangulo debe implementar un método que calcule el área del rectángulo a partir de sus dos parámetros Punto.

El programa, empleando las clases anteriores, debe crear dos objetos punto y presentar por pantalla el área del rectángulo formado por los dos puntos.

NOTA: Lógicamente el área se calcula multiplicando las diferencias entre las coordenadas x e y de ambos puntos.

Por ejemplo:

  • Punto 1: (3,5)  y Punto 2: (1,1)
  • Diferencias coordenadas x = 2, diferencias coordenadas y = 4
  • Area = 8

Si alguna de las diferencias de coordenadas fuera igual a cero, se indica por pantalla que no puede calcularse el área.

Solución propuesta:

class Punto:
    def __init__(self, x, y):
        self.x = x
        self.y = y

class Rectangulo:
    def __init__(self, punto1, punto2):
        self.punto1 = punto1
        self.punto2 = punto2
    
    def calcular_area(self):
        diferencia_x = abs(self.punto1.x - self.punto2.x)
        diferencia_y = abs(self.punto1.y - self.punto2.y)
        
        if diferencia_x == 0 or diferencia_y == 0:
            print("No se puede calcular el área, una de las diferencias de coordenadas es igual a cero.")
        else:
            area = diferencia_x * diferencia_y
            print("El área del rectángulo formado por los puntos es:", area)

# Crear dos objetos de tipo Punto
punto1 = Punto(3, 5)
punto2 = Punto(1, 1)

# Crear objeto de tipo Rectangulo con los dos puntos
rectangulo = Rectangulo(punto1, punto2)

# Calcular y mostrar el área del rectángulo
rectangulo.calcular_area()

Ejercicio 3: Clase Usuario de Red Social

Crea una clase UsuarioRedSocial para representar a los usuarios de una red social. La clase tiene los siguientes atributos: el nombre del usuario, una lista de amigos y una lista de publicaciones (llámalos con los nombres de variables que quieras).  La clase debe incluir un método que pinte por pantalla todos los parámetros del objeto UsuarioRedSocial, incluyendo los amigos que tiene y las publicaciones que ha hecho.

El programa pide al usuario que introduzca su nombre, los nombres de 3 amigos y los títulos de 3 publicaciones que haya hecho en la red Social. A continuación, con los datos introducidos crea un objeto UsuarioRedSocial y pinta por pantalla los parámetros del objeto, empleando el método de la clase UsuarioRedSocial.

Solución propuesta:

class UsuarioRedSocial:
    def __init__(self, nombre, amigos, publicaciones):
        self.nombre = nombre
        self.amigos = amigos
        self.publicaciones = publicaciones
    
    def mostrar_info(self):
        print("Nombre:", self.nombre)
        print("Amigos:", ', '.join(self.amigos))
        print("Publicaciones:")
        for publicacion in self.publicaciones:
            print("- ", publicacion)

# Solicitar al usuario que introduzca sus datos
nombre = input("Introduce tu nombre: ")
amigos = [input("Introduce el nombre de un amigo: ") for _ in range(3)]
publicaciones = [input("Introduce el título de una publicación: ") for _ in range(3)]

# Crear un objeto UsuarioRedSocial con los datos introducidos
usuario = UsuarioRedSocial(nombre, amigos, publicaciones)

# Mostrar la información del usuario
usuario.mostrar_info()

Ejercicio 4: Personajes de un juego

Define una clase Personaje para representar a los personajes de un juego. La clase tiene los atributos: nombre del personaje, nivel alcanzado y puntos de vida (llámalos con los nombres de variables que quieras).  Luego, crea las clases hijas: Guerrero y Mago con al menos un parámetro específico para cada una de ellas.

El programa pregunta al usuario si quiere crear un Personaje Guerrero o Mago. En función de lo que indique el usuario, el programa pide al usuario los datos necesarios para crear el objeto y lo crea. Finalmente, indica por pantalla que el objeto ha sido creado y pinta los parámetros del objeto.

Solución propuesta:

class Personaje:
    def __init__(self, nombre, nivel, puntos_vida):
        self.nombre = nombre
        self.nivel = nivel
        self.puntos_vida = puntos_vida
    
    def mostrar_info(self):
        print("Nombre del personaje:", self.nombre)
        print("Nivel alcanzado:", self.nivel)
        print("Puntos de vida:", self.puntos_vida)

class Guerrero(Personaje):
    def __init__(self, nombre, nivel, puntos_vida, arma):
        super().__init__(nombre, nivel, puntos_vida)
        self.arma = arma

class Mago(Personaje):
    def __init__(self, nombre, nivel, puntos_vida, hechizo):
        super().__init__(nombre, nivel, puntos_vida)
        self.hechizo = hechizo

# Solicitar al usuario qué tipo de personaje quiere crear
tipo_personaje = input("¿Quieres crear un Personaje Guerrero o Mago? ").lower()

# Solicitar al usuario los datos necesarios para crear el personaje
nombre = input("Introduce el nombre del personaje: ")
nivel = int(input("Introduce el nivel alcanzado del personaje: "))
puntos_vida = int(input("Introduce los puntos de vida del personaje: "))

# Crear el objeto correspondiente al tipo de personaje elegido por el usuario
if tipo_personaje == "guerrero":
    arma = input("Introduce el arma del guerrero: ")
    personaje = Guerrero(nombre, nivel, puntos_vida, arma)
elif tipo_personaje == "mago":
    hechizo = input("Introduce el hechizo del mago: ")
    personaje = Mago(nombre, nivel, puntos_vida, hechizo)
else:
    print("Tipo de personaje no válido.")
# Mostrar que el objeto ha sido creado y pintar los parámetros del objeto
if tipo_personaje in ["guerrero", "mago"]:
    print("El personaje {} ha sido creado:".format(tipo_personaje.capitalize()))
    personaje.mostrar_info()

Ejercicio 5: Clases Barco y Velero

Define una clase llamada Barco con los parámetros: eslora y manga. Define además otra clase llamada Velero que herede de la clase Barco y que tenga un parámetro: el número de velas. La clase Velero tiene que tener un método que calcule el precio del atraque que será igual a: ((eslora x manga) + (número de velas x 10))

El programa tiene que crear dos objetos Veleros y presentar por pantalla los datos de ambos veleros. Además, indicar para que velero es más barato el precio del atraque.

Solución propuesta:

class Barco:
    def __init__(self, eslora, manga):
        self.eslora = eslora
        self.manga = manga

class Velero(Barco):
    def __init__(self, eslora, manga, num_velas):
        super().__init__(eslora, manga)
        self.num_velas = num_velas
    
    def calcular_precio_atraque(self):
        return (self.eslora * self.manga) + (self.num_velas * 10)

# Crear dos objetos Velero
velero1 = Velero(10, 5, 3)
velero2 = Velero(8, 4, 4)

# Mostrar los datos de ambos veleros
print("Datos del Velero 1:")
print("Eslora:", velero1.eslora)
print("Manga:", velero1.manga)
print("Número de velas:", velero1.num_velas)
print("Precio del atraque:", velero1.calcular_precio_atraque())

print("\nDatos del Velero 2:")
print("Eslora:", velero2.eslora)
print("Manga:", velero2.manga)
print("Número de velas:", velero2.num_velas)
print("Precio del atraque:", velero2.calcular_precio_atraque())

# Comparar precios de atraque
if velero1.calcular_precio_atraque() < velero2.calcular_precio_atraque():
    print("\nEl Velero 1 tiene un precio de atraque más barato.")
elif velero1.calcular_precio_atraque() > velero2.calcular_precio_atraque():
    print("\nEl Velero 2 tiene un precio de atraque más barato.")
else:
    print("\nAmbos veleros tienen el mismo precio de atraque.")

Ejercicio 6: Clases Vuelo y Avión

Crea una clase Vuelo para representar los vuelos de una aerolínea. La clase tendrá los atributos: número de vuelo, número de pasajeros y destino. Implementa otra clase llamada Avion que tenga los parámetros: número de asientos y lista de destinos.

El programa pide al usuario que introduzca la información de un Vuelo y de un Avion. Con la información introducida, crea un objeto Vuelo y otro objeto Avion. Y empleando los parámetros de ambos objetos, indica si se puede asignar el avión al vuelo. Lógicamente, para que se pueda asignar un avión a un vuelo, el número de asientos del avión tiene que ser igual o superior al número de pasajeros y el destino del vuelo tiene que estar en la lista de destinos del avión.

Solución propuesta:

class Vuelo:
    def __init__(self, numero_vuelo, num_pasajeros, destino):
        self.numero_vuelo = numero_vuelo
        self.num_pasajeros = num_pasajeros
        self.destino = destino

class Avion:
    def __init__(self, num_asientos, destinos):
        self.num_asientos = num_asientos
        self.destinos = destinos

# Solicitar al usuario la información del vuelo
numero_vuelo = input("Introduce el número de vuelo: ")
num_pasajeros = int(input("Introduce el número de pasajeros: "))
destino_vuelo = input("Introduce el destino del vuelo: ")

# Solicitar al usuario la información del avión
num_asientos = int(input("Introduce el número de asientos del avión: "))
destinos_avion = input("Introduce la lista de destinos del avión separados por comas: ").split(',')

# Crear objetos Vuelo y Avion
vuelo = Vuelo(numero_vuelo, num_pasajeros, destino_vuelo)
avion = Avion(num_asientos, destinos_avion)

# Comprobar si se puede asignar el avión al vuelo
if vuelo.num_pasajeros <= avion.num_asientos and vuelo.destino in avion.destinos:
    print("El avión puede ser asignado al vuelo.")
else:
    print("El avión no puede ser asignado al vuelo.")

Ejercicio 7: Clase Vivienda y clase Chalet

Crea una clase llamada Vivienda con los parámetros: año de construcción, metros cuadrados habitables y precio. Crea otra clase llamada Chalet que herede de Vivienda y que tenga un único parámetro: piscina (un booleano que indica si el chalet tiene piscina).

La clase Chalet tiene un método para calcular el máximo de hipoteca que se le puede conceder. Este método sigue la lógica siguiente: Si el chalet tiene piscina, el máximo de hipoteca que se puede conceder es cero, si no la tiene, el máximo de hipoteca es el precio por metro cuadrado multiplicado por 100 (el precio por metro cuadrado se obtiene dividiendo los metros cuadrados habitables entre el precio).

El programa pide al usuario que introduzca los datos de un Chalet, crea un objeto Chalet con los datos introducidos, y empleando la función de la clase Chalet presenta por pantalla el máximo de hipoteca para ese Chalet.

Solución propuesta

class Vivienda:
    def __init__(self, ano_construccion, metros_cuadrados, precio):
        self.ano_construccion = ano_construccion
        self.metros_cuadrados = metros_cuadrados
        self.precio = precio

class Chalet(Vivienda):
    def __init__(self, ano_construccion, metros_cuadrados, precio, piscina):
        super().__init__(ano_construccion, metros_cuadrados, precio)
        self.piscina = piscina
    
    def calcular_max_hipoteca(self):
        if self.piscina:
            return 0
        else:
            return (self.precio / self.metros_cuadrados) * 100

# Solicitar al usuario que introduzca los datos del Chalet
ano_construccion = int(input("Introduce el año de construcción del chalet: "))
metros_cuadrados = float(input("Introduce los metros cuadrados habitables del chalet: "))
precio = float(input("Introduce el precio del chalet: "))
piscina = input("El chalet tiene piscina (si/no): ").lower() == "si"

# Crear objeto Chalet con los datos introducidos
chalet = Chalet(ano_construccion, metros_cuadrados, precio, piscina)

# Calcular y mostrar el máximo de hipoteca para el Chalet
max_hipoteca = chalet.calcular_max_hipoteca()
print("El máximo de hipoteca para este chalet es:", max_hipoteca)

Ejercicio 8: Clase Coche e hijas

Define una clase llamada Coche con los parámetros marca y modelo. Luego, define otra clase llamada Deportivo que herede de la clase Coche y que tenga un parámetro adicional: potencia. La clase Deportivo debe tener un método que calcule el precio del seguro, el cual será igual a la potencia del coche multiplicada por 20 más un extra de 1000 euros.

El programa debe crear dos objetos Deportivo y mostrar por pantalla los datos de ambos coches. Además, debe indicar cuál de los dos coches tiene un seguro más barato.

Solución propuesta:

class Coche:
    def __init__(self, marca, modelo):
        self.marca = marca
        self.modelo = modelo

class Deportivo(Coche):
    def __init__(self, marca, modelo, potencia):
        super().__init__(marca, modelo)
        self.potencia = potencia
    
    def calcular_precio_seguro(self):
        return (self.potencia * 20) + 1000

# Crear dos objetos Deportivo
deportivo1 = Deportivo("Ferrari", "Testarossa", 500)
deportivo2 = Deportivo("Porsche", "911", 400)

# Mostrar los datos de ambos coches deportivos
print("Datos del Deportivo 1:")
print("Marca:", deportivo1.marca)
print("Modelo:", deportivo1.modelo)
print("Potencia:", deportivo1.potencia)
print("Precio del seguro:", deportivo1.calcular_precio_seguro())

print("\nDatos del Deportivo 2:")
print("Marca:", deportivo2.marca)
print("Modelo:", deportivo2.modelo)
print("Potencia:", deportivo2.potencia)
print("Precio del seguro:", deportivo2.calcular_precio_seguro())

# Indicar cuál de los dos coches tiene un seguro más barato
if deportivo1.calcular_precio_seguro() < deportivo2.calcular_precio_seguro():
    print("\nEl Deportivo 1 tiene un seguro más barato.")
elif deportivo1.calcular_precio_seguro() > deportivo2.calcular_precio_seguro():
    print("\nEl Deportivo 2 tiene un seguro más barato.")
else:
    print("\nAmbos coches tienen el mismo precio de seguro.")

NOTA:

Este post es parte de la colección “Python”. Puedes ver el índice de esta colección aquí.