12. Archivos

Una definición de archivo simple puede exponerse como un conjunto de bits almacenados en un dispositivo de memoria persistente (no volatil). Cuando se habla de memoria persistente, también conocida como persistencia de datos, se refiere a la posibilidad de guardar los datos en una unidad que garantice de cierta forma que los datos no serán perdidos. Esto pasa a ser una ventaja, pues el uso de la persistencia de los datos posibilita que cualquier herramienta o programa puede acceder a el archivo en forma de lectura y pueda rescatar los datos para ser trabajados cuando sea requerido.

Los archivos son localizables dentro de un directorio que se le conoce como la ruta del archivo, en donde se identifican con un nombre (nombre del archivo) y además de manera opcional podría contar con una extensión (txt, json, sql, etc) que le permite ser identificado por el usuario o las herramientas.

Python cuenta con una serie funciones que permiten, crear o abrir un archivo, procesar o manipular los datos almacenados en el archivo, y por último, y muy importante para garantizar la integridad del archivo y los datos almacenado en él, el cerrar el archivo.

12.1. Abrir un archivo

En Python, para abrir un archivo usaremos la función "open" que requiere como argumento el nombre del archivo a abrir.

Ejemplo

archivo = open("archivo.txt")

La función "open" intentará abrir el archivo con el nombre indicado. Si se tiene éxito se asociará a una variable que nos permitirá avanzar al paso de manipular el archivo.

En caso de no poder encontrar el archivo arrojará un error de excepción

Ejemplo

Traceback (most recent call last):
File "<stdin>", line 1, in <module>
FileNotFoundError: [Errno 2] No such file or directory: 'archivo.txt'

Un archivo se puede abrir para distintos propósitos o modos (lectura, escritura, agregado) y dependerá de esto la posibilidad de acciones permitidas.

  • Lectura (r) o read, en este caso no es posible realizar modificaciones sobre el archivo y es permitido solo leer el contenido.

  • Escritura (w) o write. Este modo tiene dos propósitos, limpiar un archivo (todos los datos insertos son borrados) o crear un archivo nuevo para el caso de no existir.

  • Agregado (a) o append. Refiere a un modo de escritura en donde también se crea el archivo si no existiese, pero en caso de que exista se posiciona al final, manteniendo el contenido original.

Ejemplo

archivo = open("archivo.txt", "r")
archivo = open("archivo.txt", "w")
archivo = open("archivo.txt", "a")

En caso de que no se especifique el modo, los archivos serán abiertos en modo sólo lectura (r).

Ejemplo

archivo = open("archivo.txt")

Es importante conocer los estados en que se pueden abrir los archivos, pues puede ser bastante peligroso abrir uno en una forma incorrecta. El peligro radica en que todo el contenido podría perderse con una apertura de un archivo en modo write (w).

12.2. Procesar o Manipular un archivo

El procesamiento o manipulación del archivo es lo que realmente importa, ya que es la forma en como vamos a acceder a los datos para obtener o generación información.

Existen dos formas de procesar un archivo

  • Línea a línea

  • Volcar todo el contenido en una variable y luego procesar esa variable

12.2.1. Procesamiento linea a linea

Esta forma consiste en abrir el archivo de texto e ir iterando linea a linea y utilizando la función "readline()". La idea es ir procesando cada una de las linea para cumplir una acción.

Ejemplo

#!/usr/bin/env python3
# -*- coding:utf-8 *-*

archivo = open("nombre.txt")
linea = archivo.readline()

while linea != '':
    # procesar linea
    print(linea)
    linea = archivo.readline()
    archivo.close()

En donde:

  • Se abre un archivo llamado "nombre.txt"

  • A través del método "readline()" se obtiene la primera posición del archivo (primera línea).

  • Se itera con un ciclo "while" hasta que encuentre un fin de archivo que siempre será un carácter vacío ('').

  • Por cada una de las iteraciones se imprime y salta a la siguiente línea también utilizando el método "readline()".

  • Una vez llega al final del archivo, este a tráves del método "close()" cierra el archivo liberando de memoria todo su contenido.

Siempre hay que considerar que estamos iterando sobre elementos, por lo que es posible utilizar cualquier ciclo disponible en el lenguaje. Entonces se asume que otra forma de manipular y acceder a la data del archivo es a través de un ciclo "for"

Ejemplo

for linea in archivo:
    print(linea)

En donde "archivo" es la variable en donde se asigno la apertura del archivo y "linea" representa a cada una de las lineas con datos que posee el archivo y no siendo necesario llamar a la función "readline()" en cada iteración.

12.2.2. Volcar el contenido en una variable

Esta es otra forma de acceder al contenido del archivo. La idea es obtener leer todo el archivo utilizando la función "readlines()" que permite asignar el contenido una variable. Luego se procesa esta variable de acuerdo a la necesidad.

Ejemplo

#!/usr/bin/env python3
# -*- coding:utf-8 *-*

archivo = open("nombre.txt")
lineas = archivo.readlines()
archivo.close()
print(lineas)

En donde:

  • Se abre un archivo llamado "nombre.txt".

  • En la variable lineas y gracias a la función "readlines()" se vuelca todo el contenido del archivo seleccionado.

  • Se cierra el archivo.

  • Imprime el contenido de la variable lineas.

12.3. Escribiendo en un archivo

Existen dos formas de escribir dentro de un archivo, y estas formas son utilizando el método write o writelines.

  • write: espera una cadena como argumento y la escribe en el archivo. Si proporciona una lista de cadenas, generará una excepción y que por cierto, ¡nos mostrará errores!

  • writelines: espera un iterable como argumento (un objeto iterable puede ser una tupla, una lista, una cadena o un iterador en el sentido más general). Se espera que cada elemento contenido en el iterador sea una cadena. Una tupla de cadenas es lo que proporcionó.

La naturaleza de las cadenas de texto no es importante para ambas funciones, es decir, solo escriben en el archivo lo que sea que les proporcione. La parte interesante es que writelines() es que no agrega caracteres de nueva línea por sí mismo, por lo que el nombre del método puede ser bastante confuso. En realidad, se comporta como un método imaginario llamado write_all_of_these_strings (una secuencia).

Ejemplo:

archivo.write("Hola Mundo")
archivo.writelines(["Hola", "Mundo"])

Otro ejemplo:

lineas = ["linea 1", "linea 2"]
archivo = open("archivo.txt", "w")
for i in lineas:
    archivo.write(i)
archivo.close()

archivo = open("archivo.txt", "w")
archivo.writelines(lineas)
archivo.close()

12.4. Abrir archivos de modo seguro usando with

El método "with" nos permite ejecutar un bloque de código que una vez ejecutado permita cerrar automáticamente el archivo.

la sintaxis es:

with open("archivo.txt") as f

En donde f será la variable que se almacenará el contenido del archivo.

Ejemplo de lectura con with

with open("archivo.txt", "r") as archivo:
    lineas = archivo.readlines()
    print(lineas)

Ejemplo de escritura con with

with open("archivo.txt", "w") as archivo:
    archivo.writelines(["Hola.\n","Ha creado un archivo de forma segura."])

12.5. Comprobar existencia de un archivo

Para chequear la existencia de un archivo, se puede hacer a distintos niveles, ya sea comprobar si es un archivo o bien es un directorio. Para esto podemos hacer uso de las siguientes sintaxis:

Ejemplo de chequeo de existencia de un archivo

from pathlib import Path

my_file = Path("/path/to/file")
if my_file.is_file():
    # file existe

Ejemplo de chequeo de existencia de un directorio

from pathlib import Path

my_file = Path("/path/to/folder")
if my_file.is_dir():
    # directorio existe

Ejemplo de chequeo de una ruta completa

from pathlib import Path

my_file = Path("/path/to/file")
if my_file.exists():
    # file exists

12.6. Útiles

  • Podemos depurar el estado del archivo utilizando un print()

a = open("archivo.txt")
print(a)

La salida esperada de esto sería:

<_io.TextIOWrapper name='archivo.txt' mode='r' encoding='UTF-8'>

Otros métodos para utilizar:

  • writable(): devolverá True si el archivo está en modo de escritura.

  • readable(): Será True si el archivo está en modo de lectura.

  • seekable(): devolverá True si es posible desplazarse dentro del archivo.

  • tell(): regresará la posición en la que se encuentra el puntero dentro del archivo.

  • seek(): Moverá el puntero a la posición indicada.

12.7. Ejercicio propuesto

Cree un archivo (notas.txt) para almacenar las siguientes notas de un ramo:

Pepito:5.3:3.7:6.7:6.7:6.1:5.5
Juanito:5.5:5.2:2.0:5.6:6.0:2.0
Maria:6.1:6.6:6.4:5.1:5.8:6.3
Joselito:5.2:4.7:1.8:3.5:2.7:4.5

Cada lı́nea tiene el nombre del alumno y sus seis notas, separadas por dos puntos (:). Escriba un programa que cree un nuevo archivo llamado reporte.txt, en que cada lı́nea indique si el alumno está aprobado (promedio ≥ 4,0) o reprobado (promedio < 4,0)

Solución propuesta

#! /usr/bin/env python3
# -*- coding: utf-8 -*-


def escribe_archivo(datos):
    reporte = open("reporte.txt", 'w')
    reporte.write(datos)
    reporte.close()


def calcula_promedio(notas):
    promedio = 0
    for i in notas:
        promedio += float(i)
    promedio = promedio / len(notas)
    return promedio


def procesa_archivo(archivo):
    datos = ''
    for linea in archivo:
        cl = linea.strip().split(':')
        nombre = cl[0:1]
        # print(nombre)
        notas = cl[2:len(cl)]
        promedio = calcula_promedio(notas)
        if promedio > 4:
            datos += '{0} {1} , Aprueba\n'.format(nombre[0], promedio)
        else:
            datos += '{0} {1}, Reprueba\n'.format(nombre[0], promedio)
    escribe_archivo(datos)


if __name__ == '__main__':
    nombre_archivo = "notas.txt"
    archivo = open(nombre_archivo, "r")
    procesa_archivo(archivo)
    archivo.close()