7. Archivos de texto#
7.1. Introducción#
En el desarrollo de software, especialmente en el ámbito de la ingeniería, es común la necesidad de almacenar, recuperar y procesar información persistente. Los archivos de texto representan una de las formas más simples y universales de hacerlo.
Un archivo de texto es un archivo que contiene únicamente caracteres legibles para un ser humano. A diferencia de los archivos binarios, no requiere un formato especial para ser interpretado: puede abrirse y leerse fácilmente con cualquier editor de texto. Este tipo de archivo se utiliza ampliamente para guardar datos estructurados o no estructurados, como resultados de experimentos, datos de sensores, configuraciones, registros de eventos, o incluso scripts de simulación.
En este capítulo abordaremos cómo leer y registrar información en archivos de texto, así como una breve introducción al manejo del sistema de archivos. El dominio de estos tópicos es esencial para automatizar flujos de trabajo, procesar grandes volúmenes de datos, generar reportes o preparar entradas y salidas para análisis numéricos. Naturalmente, es frecuente que lo anterior forme parte del quehacer diario de un ingeniero en estos tiempos.
7.2. Abrir y cerrar archivos de texto#
Antes de poder leer o escribir datos en un archivo, es necesario abrirlo. En Python, esto se realiza mediante la función incorporada open. Una vez finalizadas las operaciones con el archivo, se debe cerrar explícitamente para liberar los recursos del sistema. Prácticamente, siempre que vayamos a trabajar con archivos de texto, es necesario seguir estos tres pasos:
Abrir el archivo indicando su ruta (ubicación) y el modo de trabajo. Aquí utilizamos la función
open.Leer o escribir la información. Aquí usaremos algún método para leer o escribir el archivo.
Cerrar el archivo, para esto usamos el método
close.
Por ejemplo, vamos a suponer que tenemos un archivo de texto denominado texto.txt, ubicado en la misma carpeta que nuestro script de Python (o notebook de Jupyter). De este archivo vamos a leer y mostrar su contenido, para esto primero debemos abrir el archivo utilizando la función open, la cual recibe como primer argumento la ruta y el nombre del archivo (en este caso "texto.txt"), y como segundo argumento el modo de apertura del archivo (en este caso "r", que indica modo lectura):
f = open("texto.txt","r")
Una vez abierto el archivo, podemos leer el contenido del archivo utilizando alguno de los métodos que tenemos disponibles en el objeto f creado previamente, por ejemplo, una manera sería utilizando el método read:
texto = f.read()
print(texto)
Look at the stars
Look how they shine for you
And everything you do
Yeah, they were all yellow
Una vez leído y almacenado el contenido, podemos ahora cerrar el archivo utilizando el método close.
f.close()
Otra manera de abrir y utilizar archivos es con la palabra reservada with, la cual nos sirve para crear un bloque de contexto. Veamos el mismo ejemplo anterior:
with open("texto.txt","r") as f:
texto = f.read()
print(texto)
Look at the stars
Look how they shine for you
And everything you do
Yeah, they were all yellow
La ventaja de utilizar with es que no tenemos que cerrar explícitamente el archivo, algo que puede ser muy conveniente dado que es frecuente olvidar el cierre de los archivos. Incluso si ocurre un error dentro del bloque, Python cerrará el archivo automáticamente.
7.3. Leer archivos de texto#
Para leer un archivo de texto en Python debemos abrirlo utilizando el modo de lectura ('r') y enseguida utilizar uno de los métodos disponibles para leer el contenido del archivo. Vamos a comenzar con el método read, este nos permite leer de una sola vez todo el contenido del archivo y nos lo devuelve en una cadena de texto. Para ejemplificar vamos a leer un archivo datos.txt que contiene 10 líneas con mediciones de temperaturas:
with open('datos.txt','r') as f:
datos = f.read()
print(datos)
18.5
20.1
22.3
24.0
25.6
26.4
27.0
25.8
23.1
20.7
Podemos verificar que la variable datos es de tipo str (cadena de caracteres):
print(type(datos))
<class 'str'>
Obviamente, si quisiéramos convertir esta información a una lista de valores numéricos que podamos analizar, entonces nos veríamos en la necesidad de procesar la variable datos, una manera podría ser la siguiente:
datos_num = [float(dato) for dato in datos.split()]
print(datos_num)
[18.5, 20.1, 22.3, 24.0, 25.6, 26.4, 27.0, 25.8, 23.1, 20.7]
Otro método que podemos usar para leer el contenido de un archivo de texto es readlines, el cual también lee de una sola vez todo el contenido del archivo, pero a diferencia de read devuelve una lista con todas las líneas del archivo, cada elemento de la lista es un línea. Vamos a leer el mismo archivo previo, pero ahora haciendo uso de readlines:
with open('datos.txt','r') as f:
datos = f.readlines()
print(datos)
['18.5\n', '20.1\n', '22.3\n', '24.0\n', '25.6\n', '26.4\n', '27.0\n', '25.8\n', '23.1\n', '20.7']
El método readline nos permite leer una línea del archivo cada vez que se invoca, esto es conveniente cuando el archivo es muy grande y resulta inviable leerlo todo de una vez con los métodos previos. Veamos el ejemplo:
with open('datos.txt','r') as f:
dato = f.readline()
print(dato)
18.5
Observa que en este caso únicamente estamos leyendo y mostrando la primera línea del archivo datos.txt. Si queremos leer un par de líneas, debemos entonces invocar dos veces el método readline:
with open('datos.txt','r') as f:
dato1 = f.readline()
print(dato1)
dato2 = f.readline()
print(dato2)
18.5
20.1
Para leer el archivo completo podemos utilizar un ciclo while como se muestra enseguida:
with open('datos.txt','r') as f:
dato = f.readline()
while dato:
print(dato)
dato = f.readline()
18.5
20.1
22.3
24.0
25.6
26.4
27.0
25.8
23.1
20.7
Otra manera de leer el contenido de un archivo de texto es iterando directamente sobre el archivo, tal y como se muestra enseguida:
with open('datos.txt','r') as f:
for dato in f:
print(dato)
18.5
20.1
22.3
24.0
25.6
26.4
27.0
25.8
23.1
20.7
7.4. Escribir en archivos de texto#
En ingeniería, guardar datos en archivos de texto es una operación común, la información se preserva de forma legible para una persona, pero también de forma estructurada, de tal manera que la computadora también puede leer sin complicaciones esa información.
En Python, para escribir en un archivo de texto, primero debemos abrirlo con los permisos (o modo) correspondientes de escritura. Existen dos modos de apertura que nos permiten escribir en un archivo de texto:
'w', sobrescribe el contenido existente.'a', agrega el contenido al final del archivo.
Una vez abierto el archivo podemos utilizar uno de los métodos de escritura disponibles:
f.write(cadena), nos permite escribir el contenido de una cadena de caracteres en el archivo.f.writelines(lista), nos permite escribir multiples líneas contenidas en una lista de cadenas de caracteres.
En el entendido de que f es el objeto que se crea cuando abrimos un archivo en modo de escritura.
Veamos un ejemplo utilizando el modo 'w' y el método write para escribir una cadena de caracteres a un archivo de texto:
texto = "Nunca me ha gustado ser sorprendido. Cuando me sucede algo, prefiero estar prevenido."
with open('fragmento.txt','w') as f:
f.write(texto)
Si al archivo anterior quisiéramos agregarle más texto, entonces tendríamos que utilizar el modo 'a', de lo contrario sobrescribiríamos el contenido existente. Observa el siguiente ejemplo:
mas_texto = "Te digo a media voz cosas que invento a cada rato"
with open('fragmento.txt','a') as f:
f.write(mas_texto)
Para verificar lo que hemos escrito en fragmentos.txt podemos abrirlo y leer su contenido:
with open('fragmento.txt','r') as f:
contenido = f.read()
print(contenido)
Nunca me ha gustado ser sorprendido. Cuando me sucede algo, prefiero estar prevenido.Te digo a media voz cosas que invento a cada rato
Podrás notar que las cadenas se han escrito en el archivo, una seguida de la otra, sin salto de línea de por medio. Si quisiéramos agregar un salto de línea o alguna otra separación, entonces tendríamos que agregarla de forma explícita, por ejemplo:
texto = "Los amorosos buscan,\nlos amorosos son los que abandonan,\nson los que cambian, los que olvidan."
with open("los_amorosos.txt", "w") as f:
f.write(texto)
Observa que en este caso hemos agregado saltos de línea con el caracter "\n".
with open('los_amorosos.txt','r') as f:
contenido = f.read()
print(contenido)
Los amorosos buscan,
los amorosos son los que abandonan,
son los que cambian, los que olvidan.
El método writelines nos permite escribir una lista de cadenas de caracteres a un archivo de texto, veamos el siguiente ejemplo:
paises = ["México","Colombia","Perú","Argentina","España","Chile","Venezuela"]
with open('países.txt','w') as f:
f.writelines(paises)
with open('países.txt','r') as f:
contenido = f.read()
print(contenido)
MéxicoColombiaPerúArgentinaEspañaChileVenezuela
Observa que writelines tampoco agrega un separador de líneas y por lo tanto habría que agregarlo de forma manual a cada uno de los elementos de la lista, para el ejemplo anterior:
paises = ["México","Colombia","Perú","Argentina","España","Chile","Venezuela"]
paises_con_salto = [pais+"\n" for pais in paises]
with open('países.txt','w') as f:
f.writelines(paises_con_salto)
7.5. Manejo de errores comunes#
Cuando trabajamos con archivos de texto es importante tener cuidado con errores podrían surgir, por ejemplo: intentar abrir archivos que no existen, problemas con los permisos, problemas con la codificación, entre otros. Esto nos lleva a que nuestro programa falle y detenga su ejecución, o bien, que el resultado que obtengamos no sea el correcto. En lo subsiguiente vamos a revisar cómo lidiar con algunos de estos errores.
7.5.1. Archivo no encontrado#
Es uno de los errores más comunes cuando trabajamos con archivos de texto, se produce cuando el programa intenta abrir un archivo que no existe en la ruta especificada. En Python, este tipo de error genera una excepción de tipo FileNotFoundError. Este tipo de problemas suelen presentarse porque el nombre del archivo se escribe incorrectamente, la ruta relativa o absoluta está mal especificada o bien el archivo ha sido borrado o movido de la ubicación indicada.
Observa el siguiente ejemplo, en el cual intentamos acceder a un archivo mis_datos.txt que no existe en la ubicación actual:
with open('mis_datos.txt', 'r') as f:
contenido = f.read()
print(contenido)
---------------------------------------------------------------------------
FileNotFoundError Traceback (most recent call last)
Cell In[143], line 1
----> 1 with open('mis_datos.txt', 'r') as f:
2 contenido = f.read()
4 print(contenido)
File ~\anaconda3\Lib\site-packages\IPython\core\interactiveshell.py:324, in _modified_open(file, *args, **kwargs)
317 if file in {0, 1, 2}:
318 raise ValueError(
319 f"IPython won't let you open fd={file} by default "
320 "as it is likely to crash IPython. If you know what you are doing, "
321 "you can use builtins' open."
322 )
--> 324 return io_open(file, *args, **kwargs)
FileNotFoundError: [Errno 2] No such file or directory: 'mis_datos.txt'
Una primera aproximación para lidiar con este error puede ser el uso de la sentencia try-except, tal como se muestra enseguida:
filename = "mis_datos.txt"
try:
with open(filename, 'r') as f:
contenido = f.read()
except FileNotFoundError:
print(f"El archivo '{filename}' no se encuentra en la ubicación especificada.")
El archivo 'mis_datos.txt' no se encuentra en la ubicación especificada.
Otra manera puede ser que verifiquemos primeramente que el archivo existe, por ejemplo con os.path.exists de la librería os:
import os
filename = "mis_datos.txt"
if os.path.exists(filename):
with open(filename, 'r') as f:
contenido = f.read()
else:
print(f"El archivo '{filename}' no existe.")
El archivo 'mis_datos.txt' no existe.
Naturalmente, en este caso únicamente estamos mostrando al usuario un mensaje que indica que el archivo en cuestión no existe, sin detener el flujo de ejecución del programa, sin embargo, debemos considerar que seguramente tendríamos que programar, además, las acciones a tomar si el archivo que queremos leer no se encuentra, claramente esto dependerá de cada situación particular.
A continuación, se muestra un ejemplo en el cual intentamos leer un archivo config.txt que contiene cierta información para la configuración inicial de una aplicación. En el caso de que el archivo no exista, entonces se crea un nuevo archivo de configuración que contenga unos valores predeterminados, y además dichos valores se almacenan en la variable configuracion.
archivo_configuracion = 'config.txt'
valores_predeterminados = 'A=5\nB=8\nC=10'
try:
with open(archivo_configuracion, 'r') as f:
configuracion = f.read()
except FileNotFoundError:
print("Archivo de configuración no encontrado. Se creará uno nuevo con valores predeterminados.")
with open(archivo_configuracion, 'w') as f:
f.write(valores_predeterminados)
configuracion = valores_predeterminados
print(configuracion)
Archivo de configuración no encontrado. Se creará uno nuevo con valores predeterminados.
A=5
B=8
C=10
7.5.2. Codificación#
La codificación de caracteres es el método que permite convertir un carácter de un lenguaje natural en un símbolo de otro sistema de representación, como un número o una secuencia de pulsos electrónicos en un sistema electrónico aplicando normas o reglas de codificación. [1]
En una computadora, los caracteres de un sistema de escritura se convierten en bytes para ser procesados y almacenados, y por lo tanto se requiere una representación numérica para que esto sea posible. La codificación utilizada define esa correspondencia entre caracteres y bytes. Existen varias codificaciones, a continuación se muestran algunas de las más comunes:
ASCII
ISO-8859-1 (Latin-1)
UTF-8
UTF-16
cp1252
En Python, cuando leemos o escribimos texto desde o hacia un archivo, hacemos uso de una codificación por defecto, la cual dependerá del sistema operativo y de la región en la cual estemos ubicados. El módulo locale nos permite saber qué codificación estamos utilizando por defecto:
import locale
print(locale.getpreferredencoding())
cp1252
Así pues, cualquier archivo que estemos leyendo o escribiendo lo haremos con esta codificación. Si intentáramos leer un archivo de texto que fue escrito utilizando una codificación diferente, es muy probable que tengamos problemas para representar adecuadamente algunos caracteres o bien definitivamente no poder representarlos. Este es un error frecuente cuando se trabaja con archivos de texto, puesto que muchas veces se descuida el hecho de indicar explícitamente la codificación a utilizar para evitar problemas de compatibilidad entre diversos usuarios.
Observa el siguiente ejemplo, en cual leemos un archivo de texto que fue guardado con la codificación UTF-8:
with open("fragmento-stoner.txt","r") as f:
contenido = f.read()
print(contenido)
Se empezó a preguntar si su vida merecÃa la pena, si alguna vez la habÃa merecido.
Era una duda, sospechaba, que le llegaba a todo el mundo tarde o temprano.
Se preguntaba si a los demás les sobrevenÃa con la misma fuerza impersonal que le llegaba a él.
Notarás que aparecen algunos caracteres extraños, esto debido a que por defecto en nuestra computadora se utiliza la codificación cp1252 para abrir los archivos de texto. Sin embargo, es muy sencillo especificar la codificación que queremos utilizar para abrir un archivo de texto, para esto hacemos uso del keyword argument encoding, tal como se muestra enseguida:
with open("fragmento-stoner.txt","r",encoding="utf-8") as f:
contenido = f.read()
print(contenido)
Se empezó a preguntar si su vida merecía la pena, si alguna vez la había merecido.
Era una duda, sospechaba, que le llegaba a todo el mundo tarde o temprano.
Se preguntaba si a los demás les sobrevenía con la misma fuerza impersonal que le llegaba a él.
Como puedes ver, ahora los caracteres se representan de la forma correcta. La recomendación es que siempre indiques de forma explícita la codificación que utilizarás para leer/escribir un archivo, esto evitará que tengas problemas de compatibilidad entre diversos sistemas operativos o entre usuarios ubicados en otras regiones cuya codificación por defecto podría ser distinta.
7.6. Manejo del sistema de archivos#
En ingeniería, además de trabajar con archivos de textos, muchas veces es necesario interactuar con el sistema de archivos para obtener, guardar u organizar la información, es decir, nos vemos en la necesidad de buscar en determinadas ubicaciones, crear, mover, copiar o eliminar tanto carpetas como archivos. En esta sección veremos cómo utilizar algunos de los módulos que proporciona la librería estándar de Python (os, shutil y pathlib) que nos permiten realizar operaciones con las carpetas y archivos de nuestra computadora.
Para simplificar un poco las líneas de código que escribiremos posteriormente, vamos a importar en este punto los tres módulos que utilizaremos, sólo ten la precaución de que si estás ejecutando los códigos en un script *.py, entonces deberás colocar las instrucciones de importación al inicio.
import os
import shutil
import pathlib
7.6.1. Explorar directorios y archivos#
Una de las operaciones más básicas que hacemos al trabajar con directorios es determinar el directorio actual de trabajo, esto es necesario debido a que la ubicación de ciertos archivos que necesitamos leer o escribir usualmente está establecida de forma relativa a la carpeta de proyecto en el cual estamos trabajando o al script desde el cual se requiere acceder a la información. En Python, podemos hacer uso de la función getcwd del módulo os para obtener el directorio actual de trabajo:
print( os.getcwd() )
C:\Users\delos\Google Drive\My Drive\Apuntes\Apuntes - Python\apuntes-python
Otra operación básica muy común es listar el contenido (archivos y carpetas) de un directorio específico. Observa el siguiente ejemplo, en el cual vamos a listar el contenido de una carpeta llamada img, ubicada en el directorio actual de trabajo:
os.listdir("img")
['cover.pdf',
'cover.svg',
'logo.svg',
'logo.png',
'estructuras-de-control',
'variables',
'cadenas',
'numpy',
'estructuras-de-datos',
'no.svg.txt']
Observa que en este caso tenemos algunos archivos (cover.pdf, cover.svg, logo.svg, etc.) y carpetas (variables, cadenas, numpy, etc.)
Obviamente lo anterior únicamente nos permite ver el contenido de la carpeta indicada, pero no el de las subcarpetas que contiene, por ejemplo, ahí no podemos ver si la carpeta variables contiene algún archivo u otra subcarpeta. Si queremos explorar de forma recursiva todo el contenido de un directorio, incluyendo el de sus subcarpetas, podemos utilizar la función walk del módulo os. Veamos un ejemplo:
for contenido in os.walk("img"):
print(contenido)
('img', ['estructuras-de-control', 'variables', 'cadenas', 'numpy', 'estructuras-de-datos'], ['cover.pdf', 'cover.svg', 'logo.svg', 'logo.png', 'no.svg.txt'])
('img\\estructuras-de-control', [], ['estructuras-de-control.svg'])
('img\\variables', [], ['variables_3.svg', 'variables_1.svg', 'variables_2.svg'])
('img\\cadenas', [], ['strings_index.svg', 'strings_negative_index.svg', 'slicing_2.svg', 'slicing_3.svg', 'slicing_4.svg', 'slicing.svg'])
('img\\numpy', [], ['indexing.svg', 'indexing_matrix.svg'])
('img\\estructuras-de-datos', [], ['lista-index.svg'])
La función os.walk nos devuelve un generador que podemos recorrer con un ciclo for. Cada elemento que el generador nos devuelve es una tupla de la forma:
(dirpath, dirnames, filenames)
Donde dirpath es la ruta a cada una de las carpetas contenidas en el directorio raíz pasado como argumento a walk, incluyendo el propio directorio raíz. dirnames es una lista con el nombre de cada una de las subcarpetas contenidas en dirpath y filenames una lista con el nombre (incluyendo la extensión) de los archivos contenidos en dirpath.
Así pues, en el ejemplo anterior podemos reemplazar la variable de ciclo contenido por tres variables carpeta, subcarpetas,archivos que correspondan a cada uno de los elementos de la tupla devuelta por walk:
for carpeta,subcarpetas,archivos in os.walk("img"):
print(f"{carpeta} tiene {len(subcarpetas)} subcarpetas y {len(archivos)} archivos")
img tiene 5 subcarpetas y 5 archivos
img\estructuras-de-control tiene 0 subcarpetas y 1 archivos
img\variables tiene 0 subcarpetas y 3 archivos
img\cadenas tiene 0 subcarpetas y 6 archivos
img\numpy tiene 0 subcarpetas y 2 archivos
img\estructuras-de-datos tiene 0 subcarpetas y 1 archivos
Observa que en el código anterior hemos simplemente contado la cantidad de archivos y subcarpetas en cada uno de los directorios de img. Ahora veamos un ejemplo en el cual contaremos la cantidad de archivos .svg que hay dentro de un directorio específico (incluyendo sus subcarpetas):
contador = 0
for carpeta,subcarpetas,archivos in os.walk("img"):
for archivo in archivos:
if ".svg" in archivo:
print(f"Archivo SVG encontrado: {archivo}")
contador += 1
print(f"Se encontraron en total {contador} archivos SVG")
Archivo SVG encontrado: cover.svg
Archivo SVG encontrado: logo.svg
Archivo SVG encontrado: no.svg.txt
Archivo SVG encontrado: estructuras-de-control.svg
Archivo SVG encontrado: variables_3.svg
Archivo SVG encontrado: variables_1.svg
Archivo SVG encontrado: variables_2.svg
Archivo SVG encontrado: strings_index.svg
Archivo SVG encontrado: strings_negative_index.svg
Archivo SVG encontrado: slicing_2.svg
Archivo SVG encontrado: slicing_3.svg
Archivo SVG encontrado: slicing_4.svg
Archivo SVG encontrado: slicing.svg
Archivo SVG encontrado: indexing.svg
Archivo SVG encontrado: indexing_matrix.svg
Archivo SVG encontrado: lista-index.svg
Se encontraron en total 16 archivos SVG
Observa que el código anterior es bastante mejorable, puesto que está contando por ahí un archivo no.svg.txt, el cual realmente es un archivo de texto *.txt. Modificamos un poco para evitar ese detalle:
contador = 0
for carpeta,subcarpetas,archivos in os.walk("img"):
for archivo in archivos:
if archivo.endswith(".svg"):
print(f"Archivo SVG encontrado: {archivo}")
contador += 1
print(f"Se encontraron en total {contador} archivos SVG")
Archivo SVG encontrado: cover.svg
Archivo SVG encontrado: logo.svg
Archivo SVG encontrado: estructuras-de-control.svg
Archivo SVG encontrado: variables_3.svg
Archivo SVG encontrado: variables_1.svg
Archivo SVG encontrado: variables_2.svg
Archivo SVG encontrado: strings_index.svg
Archivo SVG encontrado: strings_negative_index.svg
Archivo SVG encontrado: slicing_2.svg
Archivo SVG encontrado: slicing_3.svg
Archivo SVG encontrado: slicing_4.svg
Archivo SVG encontrado: slicing.svg
Archivo SVG encontrado: indexing.svg
Archivo SVG encontrado: indexing_matrix.svg
Archivo SVG encontrado: lista-index.svg
Se encontraron en total 15 archivos SVG
El módulo pathlib proporciona la clase Path, la cual nos permite también listar o explorar el contenido de un directorio, de una forma incluso un poco más moderna y conveniente. Veamos un ejemplo:
for elemento in pathlib.Path("img").rglob("*"):
print(elemento)
img\cover.pdf
img\cover.svg
img\logo.svg
img\logo.png
img\estructuras-de-control
img\variables
img\cadenas
img\numpy
img\estructuras-de-datos
img\no.svg.txt
img\estructuras-de-control\estructuras-de-control.svg
img\variables\variables_3.svg
img\variables\variables_1.svg
img\variables\variables_2.svg
img\cadenas\strings_index.svg
img\cadenas\strings_negative_index.svg
img\cadenas\slicing_2.svg
img\cadenas\slicing_3.svg
img\cadenas\slicing_4.svg
img\cadenas\slicing.svg
img\numpy\indexing.svg
img\numpy\indexing_matrix.svg
img\estructuras-de-datos\lista-index.svg
El método rglob de Path nos permite explorar de forma recursiva todo el contenido de un directorio especificado. El caracter * pasado como argumento indica que estamos buscando cualquier elemento. El método rglob es muy poderoso y nos permite pasar un patrón que puede expresar de forma concisa que estamos buscando ciertos tipos de directorios o archivos. Observa el siguiente ejemplo, en el cual buscamos archivos SVG que estén contenidos únicamente en subcarpetas que comiencen con la letra v:
for archivo_svg in pathlib.Path("img").rglob("v*/*.svg"):
print(archivo_svg)
img\variables\variables_3.svg
img\variables\variables_1.svg
img\variables\variables_2.svg
Una de las ventajas de usar pathlib.Path.glob es que nos devuelve un generador de objetos de la clase Path con los cuales podemos hacer operaciones adicionales. Observa el siguiente ejemplo en el cual listamos cada uno de los de archivos con el tamaño correspondiente, esta información puede accederse directamente a través del objeto archivo que resulta en cada iteración:
for archivo in pathlib.Path("img").rglob("v*/*.svg"):
file_name = archivo.name
file_size = archivo.stat().st_size
print(f"Archivo: {file_name} | Tamaño: {file_size} bytes")
Archivo: variables_3.svg | Tamaño: 9958 bytes
Archivo: variables_1.svg | Tamaño: 12025 bytes
Archivo: variables_2.svg | Tamaño: 7936 bytes
7.6.2. Crear, eliminar, renombrar y mover carpetas#
La creación y eliminación de carpetas es una operación muy común cuando trabajamos almacenando u organizando archivos. En Python disponemos de la función mkdir del módulo os para poder crear carpetas.
Veamos un ejemplo, vamos a crear una carpeta llamada demo dentro de nuestro directorio actual de trabajo:
os.mkdir("demo")
Obviamente en este punto la carpeta demo no contiene ningún archivo o subcarpeta y lo podemos verificar con os.listdir:
os.listdir("demo")
[]
Si intentamos crear nuevamente la carpeta con la función mkdir Python nos lanzará un FileExistsError, indicando que la carpeta ya existe:
os.mkdir("demo")
---------------------------------------------------------------------------
FileExistsError Traceback (most recent call last)
Cell In[297], line 1
----> 1 os.mkdir("demo")
FileExistsError: [WinError 183] Cannot create a file when that file already exists: 'demo'
Podemos crear subcarpetas dentro de demo, observa el siguiente ejemplo en donde creamos tres subcarpetas:
os.mkdir("demo/sensor_01")
os.mkdir("demo/sensor_02")
os.mkdir("demo/sensor_03")
os.listdir("demo")
['sensor_01', 'sensor_02', 'sensor_03']
Ahora observa el siguiente ejemplo:
os.mkdir("demo/sensor_04/mediciones")
---------------------------------------------------------------------------
FileNotFoundError Traceback (most recent call last)
Cell In[303], line 1
----> 1 os.mkdir("demo/sensor_04/mediciones")
FileNotFoundError: [WinError 3] The system cannot find the path specified: 'demo/sensor_04/mediciones'
Python nos devuelve un FileNotFoundError porque estamos intentando crear una carpeta llamada mediciones dentro de una carpeta denominada sensor_04 ubicada en demo, sin embargo, la carpeta sensor_04 actualmente no existe y la función os.mkdir no puede resolver este detalle. Esta situación se puede resolver utilizando la función os.makedirs:
os.makedirs("demo/sensor_04/mediciones")
La ventaja de makedirs es que puede crear todas las carpetas intermedias que hagan falta. Además de esto, makedirs puede manejar también el caso cuando una carpeta ya existe y la intentamos crear, observa el siguiente ejemplo:
os.makedirs("demo", exist_ok=True)
Bueno, sabemos que efectivamente demo ya existe, con el argumento exist_ok=True la función makedirs evita lanzar una excepción si el directorio ya ha sido creado.
Ahora veremos cómo eliminar una carpeta. El módulo os nos proporciona la función rmdir a la cual únicamente le pasamos como argumento la ruta de la carpeta que queremos eliminar. Observa el siguiente ejemplo en el cual eliminaremos la carpeta sensor_03 ubicada dentro de demo:
print( os.listdir("demo") ) # listamos contenido de demo antes de eliminar sensor_03
print("Borramos demo/sensor_03")
os.rmdir("demo/sensor_03")
print( os.listdir("demo") )
['sensor_01', 'sensor_02', 'sensor_03', 'sensor_04']
Borramos demo/sensor_03
['sensor_01', 'sensor_02', 'sensor_04']
Tratamos de hacer lo mismo con sensor_04:
os.rmdir("demo/sensor_04")
print( os.listdir("demo") )
---------------------------------------------------------------------------
OSError Traceback (most recent call last)
Cell In[316], line 1
----> 1 os.rmdir("demo/sensor_04")
2 print( os.listdir("demo") )
OSError: [WinError 145] The directory is not empty: 'demo/sensor_04'
Observa que en este caso la función os.rmdir nos devuelve un OSError, debido a que no es capaz de borrar carpetas que no estén vacías, recuerda que en este caso sensor_04 contiene una subcarpeta denominada mediciones.
Para borrar carpetas que no estén vacías podemos hacer uso de la función rmtree del módulo shutil:
shutil.rmtree("demo/sensor_04")
print( os.listdir("demo") )
['sensor_01', 'sensor_02']
Hay que tener precaución con esta función, puesto que borrará todo el contenido de la carpeta.
Para renombrar una carpeta podemos utilizar la función os.rename, la cual recibe dos argumentos: src y dst, donde src es la ruta de la carpeta a renombrar y dst la nueva ruta (incluyendo el nuevo nombre). Por ejemplo, vamos a suponer que queremos renombrar la carpeta sensor_02 (ubicada dentro de demo) como s2, entonces:
os.rename("demo/sensor_02", "demo/s2")
print( os.listdir("demo") )
['sensor_01', 's2']
La misma función os.rename nos puede servir también para mover una carpeta, simplemente cambiando la ruta en el segundo argumento dst que recibe. Para ejemplificar vamos a crear una carpeta mediciones dentro de demo:
os.mkdir("demo/mediciones")
print(os.listdir("demo"))
['sensor_01', 's2', 'mediciones']
Y ahora moveremos esta carpeta mediciones para esté contenida dentro de s2:
os.rename("demo/mediciones","demo/s2/mediciones")
Verificamos que en efecto ahora mediciones está dentro de s2:
for elemento in pathlib.Path("demo").rglob("*"):
print(elemento)
demo\sensor_01
demo\s2
demo\s2\mediciones
Hay que tener cuidado con la función os.rename, la carpeta que queremos renombrar o mover debe existir, de lo contrario nos lanzará un FileNotFoundError:
os.rename("demo/sensor_04","demo/s4")
---------------------------------------------------------------------------
FileNotFoundError Traceback (most recent call last)
Cell In[337], line 1
----> 1 os.rename("demo/sensor_04","demo/s4")
FileNotFoundError: [WinError 2] The system cannot find the file specified: 'demo/sensor_04' -> 'demo/s4'
El módulo shutil también nos permite tanto mover como copiar carpetas. Con shutil.move podemos mover una carpeta, pasándole como argumentos el origen y el destino:
shutil.move("demo/s2/mediciones", "demo/sensor_01/mediciones")
for elemento in pathlib.Path("demo").rglob("*"):
print(elemento)
demo\sensor_01
demo\s2
demo\sensor_01\mediciones
Observa que ahora la carpeta mediciones se encuentra dentro de sensor_01. Con shutil.copytree podemos copiar una carpeta y todo su contenido de un directorio a otro. Por ejemplo, vamos a copiar la carpeta sensor_01 a la carpeta s2:
shutil.copytree("demo/sensor_01", "demo/s2/sensor_01")
for elemento in pathlib.Path("demo").rglob("*"):
print(elemento)
demo\sensor_01
demo\s2
demo\sensor_01\mediciones
demo\s2\sensor_01
demo\s2\sensor_01\mediciones
Podrás notar que dentro de demo/s2 tenemos una carpeta sensor_01, además de la carpeta sensor_01 que ya teníamos en demo.
7.6.3. Mover, copiar y eliminar archivos#
Para explicar cómo mover, copiar y eliminar archivos, vamos a crear primero un conjunto de carpetas y archivos que usaremos para la demostración. Primero, vamos a crear una carpeta sensor_data dentro de nuestra carpeta demo:
sensor_data_dir = os.path.join('demo','sensor_data')
os.mkdir(sensor_data_dir)
Y ahora creamos algunos archivos txt y los ponemos dentro de demo/sensor_data:
sensores = ["sensor_01","sensor_02"]
meses = ["enero","febrero","marzo","abril"]
for sensor in sensores:
for mes in meses:
filename = os.path.join(sensor_data_dir, f"{sensor}_{mes}.txt")
with open(filename,"w") as f:
f.write("Only for test")
Podemos listar los archivos recién creados usando os.listdir:
os.listdir(sensor_data_dir)
['sensor_01_enero.txt',
'sensor_01_febrero.txt',
'sensor_01_marzo.txt',
'sensor_01_abril.txt',
'sensor_02_enero.txt',
'sensor_02_febrero.txt',
'sensor_02_marzo.txt',
'sensor_02_abril.txt']
Finalmente, vamos a crear algunas carpetas: sensor_01, sensor_02 y backup, dentro de demo/sensor_data:
os.mkdir(os.path.join(sensor_data_dir,'sensor_01'))
os.mkdir(os.path.join(sensor_data_dir,'sensor_02'))
os.mkdir(os.path.join(sensor_data_dir,'backup'))
os.listdir(sensor_data_dir)
['sensor_01_enero.txt',
'sensor_01_febrero.txt',
'sensor_01_marzo.txt',
'sensor_01_abril.txt',
'sensor_02_enero.txt',
'sensor_02_febrero.txt',
'sensor_02_marzo.txt',
'sensor_02_abril.txt',
'sensor_01',
'sensor_02',
'backup']
Perfecto, hasta acá tenemos ya creado el conjunto de archivos y carpetas que usaremos para mostrar cómo realizar las operaciones con archivos.
Comenzaremos considerando el siguiente escenario: debemos mover todos los archivos txt a la carpeta que le corresponde de acuerdo con el número de sensor, por ejemplo, sensor_01_enero.txt tenemos que moverlo a demo/sensor_data/sensor_01.
Usaremos shutil.move para efectuar esta operación. Comenzaremos con un sólo archivo:
src = os.path.join(sensor_data_dir,'sensor_01_enero.txt')
dst_dir = os.path.join(sensor_data_dir,'sensor_01')
dst = os.path.join(dst_dir,'sensor_01_enero.txt')
shutil.move(src, dst)
'demo\\sensor_data\\sensor_01\\sensor_01_enero.txt'
Y ahora podemos verificar que sensor_01_enero.txt se encuentra dentro de demo/sensor_data/sensor_01:
for elemento in pathlib.Path(sensor_data_dir).rglob("*"):
print(elemento)
demo\sensor_data\sensor_01_febrero.txt
demo\sensor_data\sensor_01_marzo.txt
demo\sensor_data\sensor_01_abril.txt
demo\sensor_data\sensor_02_enero.txt
demo\sensor_data\sensor_02_febrero.txt
demo\sensor_data\sensor_02_marzo.txt
demo\sensor_data\sensor_02_abril.txt
demo\sensor_data\sensor_01
demo\sensor_data\sensor_02
demo\sensor_data\backup
demo\sensor_data\sensor_01\sensor_01_enero.txt
Movamos ahora el resto de archivos:
for archivo in pathlib.Path(sensor_data_dir).glob("*.txt"):
if "sensor_01" in archivo.name:
shutil.move(archivo, os.path.join(sensor_data_dir,'sensor_01'))
elif "sensor_02" in archivo.name:
shutil.move(archivo, os.path.join(sensor_data_dir,'sensor_02'))
Y volvemos a verificar en dónde están nuestros archivos txt:
for elemento in pathlib.Path(sensor_data_dir).rglob("*"):
print(elemento)
demo\sensor_data\sensor_01
demo\sensor_data\sensor_02
demo\sensor_data\backup
demo\sensor_data\sensor_01\sensor_01_enero.txt
demo\sensor_data\sensor_01\sensor_01_febrero.txt
demo\sensor_data\sensor_01\sensor_01_marzo.txt
demo\sensor_data\sensor_01\sensor_01_abril.txt
demo\sensor_data\sensor_02\sensor_02_enero.txt
demo\sensor_data\sensor_02\sensor_02_febrero.txt
demo\sensor_data\sensor_02\sensor_02_marzo.txt
demo\sensor_data\sensor_02\sensor_02_abril.txt
Observa que ahora todos los archivos están en la carpeta que les corresponde.
¿Qué pasa si quisiéramos hacer un respaldo de todos los archivos txt existentes? Para esto podemos hacer copias de los archivos y colocarlas en la carpeta backup. La función shutil.copy nos permite realizar esta operación:
for archivo in pathlib.Path(sensor_data_dir).rglob("sensor_*/*.txt"):
shutil.copy(archivo, os.path.join(sensor_data_dir,'backup'))
Verificamos que en backup tenemos una copia de los archivos
for elemento in pathlib.Path(sensor_data_dir).rglob("*"):
print(elemento)
demo\sensor_data\sensor_01
demo\sensor_data\sensor_02
demo\sensor_data\backup
demo\sensor_data\sensor_01\sensor_01_enero.txt
demo\sensor_data\sensor_01\sensor_01_febrero.txt
demo\sensor_data\sensor_01\sensor_01_marzo.txt
demo\sensor_data\sensor_01\sensor_01_abril.txt
demo\sensor_data\sensor_02\sensor_02_enero.txt
demo\sensor_data\sensor_02\sensor_02_febrero.txt
demo\sensor_data\sensor_02\sensor_02_marzo.txt
demo\sensor_data\sensor_02\sensor_02_abril.txt
demo\sensor_data\backup\sensor_01_enero.txt
demo\sensor_data\backup\sensor_01_febrero.txt
demo\sensor_data\backup\sensor_01_marzo.txt
demo\sensor_data\backup\sensor_01_abril.txt
demo\sensor_data\backup\sensor_02_enero.txt
demo\sensor_data\backup\sensor_02_febrero.txt
demo\sensor_data\backup\sensor_02_marzo.txt
demo\sensor_data\backup\sensor_02_abril.txt
Para eliminar un archivo podemos utilizar os.remove. En la siguiente celda vamos a eliminar el archivo sensor_01_enero.txt contenido en la carpeta de backup:
file_path = os.path.join(sensor_data_dir,'backup','sensor_01_enero.txt')
os.remove( file_path )
Verificamos con os.listdir:
os.listdir(os.path.join(sensor_data_dir,'backup'))
['sensor_01_febrero.txt',
'sensor_01_marzo.txt',
'sensor_01_abril.txt',
'sensor_02_enero.txt',
'sensor_02_febrero.txt',
'sensor_02_marzo.txt',
'sensor_02_abril.txt']
7.7. Ejercicios#
Escribe un programa que, dado un archivo datos.txt con formato:
Juan,25
Ana,30
Luis,22
Convierte los datos en una lista de diccionarios, por ejemplo, para el archivo de ejemplo debería devolver:
[{"nombre": "Juan", "edad": 25}, {"nombre": "Ana", "edad": 30}, {"nombre": "Luis", "edad": 22}]
Escribe una función que reciba como argumento la ruta de un archivo txt y que devuelva la palabra que más se repite en el texto y cuántas veces lo hace.
Desarrolla un programa que dado un directorio cuente y muestre la cantidad de archivos, agrupados por tipo.
Imagina que tienes una carpeta llamada Descargas con archivos .jpg, .pdf y .txt. Escribe un script que:
Verifique si existen las carpetas
Imágenes,DocumentosyTexto. Si no existen, que las cree.Mueva cada archivo de la carpeta Descargas a su carpeta correspondiente según su extensión.