Tabla de Contenidos

Conceptos básicos de archivos y directorios

Definición de direcciones de archivo o directorio

Un archivo tiene habitualmente dos key properties: un nombre de archivo o filename, y un directorio o path donde se encuentra.

La estructura de archivos y directorios es diferente en distintos Sistemas Operativos, debemos conocer cómo se organiza en el que estamos trabajando.

Además, el símbolo para separar directorios y subdirectorios, también cambia entre Windows (\) y Linux o Mac (/).
Para que nuestro programa funcione en cualquier Sistema Operativo, podemos usar la función Path() de la biblioteca pathlib.

Path() devuelve un objeto de tipo Path, que al pasarlo a string, devolverá la cadena en función del Sistema Operativo en el que se esté ejecutando el programa. Al definir direcciones en el programa (usando Path), se usa / por convención, ya que entre los desarrolladores, suele ser linux el Sistema Operativo preferente.

from pathlib import Path
myFiles = ['accounts.txt', 'details.csv', 'invite.docx']
for filename in myFiles:
    print(Path(r'C:/Users/Al', filename))  # Con Path() se pueden concatenar cadenas que definan el directorio. 
 
# C:\Users\Al\accounts.txt
# C:\Users\Al\details.csv
# C:\Users\Al\invite.docx

La biblioteca pathlib fue introducida en Python 3.4, reemplazando al viejo os.path de versiones anteriores.

Concatenar directorios con /

Del mismo modo que se pueden concatenar strings con el símbolo +, se pueden concatenar paths con el símbolo /.

from pathlib import Path
homeFolder = Path('C:/Users/Al')
subFolder = Path('spam')
homeFolder / subFolder       # WindowsPath('C:/Users/Al/spam') # Objeto tipo Path
str(homeFolder / subFolder)  # 'C:\\Users\\Al\\spam' # Hay dobles backlashes, ya que estamos en Windows y el primer \ es el carácter de escape

Sólo hay que tener en cuenta al unir paths con /, es que uno de los dos primeros valores que se pasan deben ser de tipo Path. Si son strings, por ejemplo, se produce un error TypeError.

El operador / reemplaza al viejo os.path.join() de versiones de Python anteriores.

Direcciones absolutas y relativas

Hay dos formas de especificar un directorio:

Además se puede indicar:

Directorio de trabajo y directorio de usuario

El directorio de trabajo es el directorio desde el que estamos trabajando, también denominado Current Working Directory o cwd.

El directorio de usuario depende del Sistema Operativo, y es donde se guarda la información del usuario. Se recomienda usar este directorio para poner nuestros programas, ya que así nos aseguramos de tener todos los permisos necesarios.

Creación y manejo de directorios

Creación de directorios

Se pueden crear directorios, así como los directorios intermedios definifos si no existen, con la función os.makedirs(). Se pasa como argumento un string con la dirección completa.

También pueden crearse directorios con el método de Path, mkdir():

from pathlib import Path
Path(r'C:\Users\Al\spam').mkdir() # mkdir() no crea directorios intermedios, para eso usar os.makedirs()

Trabajar con direcciones absolutas y relativas

El módulo (biblioteca) pathlib proporciona los siguientes métodos para comprobar si un path es absoluto o relativo:

Otras funciones útiles:

Partes de una dirección de archivo

Un objeto tipo Path se compone de diferentes partes, que constituyen sus atributos:
c:\Users\Al\spam.txt

Para obtener cada atributo, es tan fácil como indicarlo:

p = Path('C:/Users/Al/spam.txt')
p.anchor  # 'C:\\'
p.parent  # WindowsPath('C:/Users/Al')  # parent es el único que es de tipo Path en lugar de string
p.name    # 'spam.txt'
p.stem    # 'spam'
p.suffix  # '.txt'
p.drive   # 'C:'

También se puede utilizar el módulo viejo os:

calcFilePath = 'C:\\Windows\\System32\\calc.exe'
calcFilePath.split(os.sep)
# ['C:', 'Windows', 'System32', 'calc.exe']

Buscando tamaños de archivo y contenido de directorios

Se pueden buscar tamaños de archivo y contenido de directorios usando el módulo os:

También se pueden usar patrones Glob para obtener archivos de un directorio. Los patrones Glob pueden realizar búsquedas de forma similar a la de las expresiones regulares, aunque es más sencillo:

p = Path('C:/Users/Al/Desktop')
p.glob('*') # <generator object Path.glob at 0x000002A6E389DED0> Devuelve un objeto generador
list(p.glob('*')) # Crea una lista de los archivos que se encuentran en el directorio definido en p, en formato absoluto

como argumentos de glob() se especifica el patrón de forma similar a una expresión regular:

Validación de paths

Muchas funciones dan un error si la dirección indicada no funciona. Algunos métodos útiles de tipos de objeto Path, son (asumiendo p como objeto tipo Path):

Para estos mismos cometidos podrían usarse los viejos métodos os.path.exists(path), os.path.isfile(path), y os.path.isdir(path).

Proceso de lectura/escritura de archivos

En este apartado se verá la forma de leer y escribir en archivos de texto plano.

Lectura/escritura básica

Los métodos de la clase pathlib más sencillos y directos para leer/escribir con archivos son path.read_text() y path.write_text(string).

Lectura/escritura con open()

Proceso a seguir:

  1. Se llama a la función open(), que devuelve un objeto de tipo File: archivo = open(dirección del archivo)
  2. Se llama a los métodos read() o write() del objeto File.
  3. Se cierra el archivo con el método close() del objeto File.

Lectura

La función open() permite un segundo argumento en el que se indica el modo en el que se abre el archivo, por defecto en modo lectura (argumento: 'r').

El método read() devuelve en un único string el contenido de todo el archivo. Con el método readlines() se obtiene una lista en la que cada elemento es el string correspondiente a cada línea del archivo (cada string termina con \n, salvo el último, lógicamente).

Escritura

Para escribir texto en un archivo de texto plano, éste debe de abrirse con open() indicando el modo:

En ambos casos, siempre se escribe la cadena que se pasa como argumento del método write(). Devuelve el número de caracteres escritos.

Sentencia with

Con el fin de tener un código más limpio, es común a la hora de trabajar con directorios usar la sentencia with, que nos permite evitar tener que estar cerrando el archivo con 'close()'.
Un ejemplo:

with open('file_path', 'w') as file:
    file.write('hello world !')

Guardar variables

Guardar variables en archivos binarios

Con el módulo shelve se pueden guardar variables en un archivo binario, para después recuperar sus valores. De ese modo, no se pierde informaión al cerrar el programa (las variables normales son destruidas).

La forma de usar esta funcionalidad es similar a la de leer/escribir en un archivo:

  1. Importamos la biblioteca: import shelve
  2. Damos un nombre a los archivos binarios que se crearán ('mydata'), los abrimos (con shelve se permite tanto leer como escribir) y asignamos el valor devuelto a una variable: shelfFile = shelve.open('mydata')
  3. Trabajamos con la información que contiene:
    1. Para escribir (guardar) información, se guarda como se haría en un diccionario.
    2. Para leer (recuperar) información, también se hace como en un diccionario: atendiendo a la clave entre corchetes.
    3. Es posible listar la información con la función list()
  4. Cerramos el binario con close(): ShelfFile.close()
import shelve
shelfFile = shelve.open('mydata')
cats = ['Zophie', 'Pooka', 'Simon']
shelfFile['cats'] = cats
shelfFile['cats']        # ['Zophie', 'Pooka', 'Simon']
list(shelfFile.keys())   # ['cats']
list(shelfFile.values()) # [['Zophie', 'Pooka', 'Simon']]
shelfFile.close()

Guardar variables en archivos de texto .py

En este caso la idea es guardar las variables en un archivo de python (.py), de forma que pueda importarse como una biblioteca, y de ese modo recuperar los dartos.
Para ello se utiliza el método pformat() de la biblioteca pprint (Pretty Print), que devuelve una variable tal y cmo se escribe en código.

import pprint
cats = [{'name': 'Zophie', 'desc': 'chubby'}, {'name': 'Pooka', 'desc': 'fluffy'}]
pprint.pformat(cats)  # "[{'desc': 'chubby', 'name': 'Zophie'}, {'desc': 'fluffy', 'name': 'Pooka'}]"
fileObj = open('myCats.py', 'w')
fileObj.write('cats = ' + pprint.pformat(cats) + '\n')  # 83
fileObj.close()
import myCats
myCats.cats             # [{'name': 'Zophie', 'desc': 'chubby'}, {'name': 'Pooka', 'desc': 'fluffy'}]
myCats.cats[0]          # {'name': 'Zophie', 'desc': 'chubby'}
myCats.cats[0]['name']  # 'Zophie'

Con esta funcionalidad los datos se guardan en texto plano, fácil de editar por cualquiera.
De este modo se pueden guardar variables de tipos básicos (string, enteros, listas, diccionarios, etc), pero no de tipos más complejos (como Files).
Para guardar datos más complejos es necesario usar el módulo shelve.

Trabajar con archivos y directorios

Acciones sobre archivos y directorios

Para realizar acciones sobre archivos y directorios, como mover, copiar, renombrar o borrar, vamos a utilizar la biblioteca shutil shell utilities.

Copiar

Para copiar un único archivo se utiliza el método shutil.copy(source, destination).

Para copiar un directorio completo, con todos sus subdirectorios y archivos, se utiliza del mismo modo la función shutil.copytree(source, destination). Devuelve un dato Path con el dirección del directorio copiado.

Mover y renombrar

Para mover un archivo o un directorio, se utiliza el método shutil.move(source, destination):

Borrado de directorios y archivos

Borrado permanente

import os
from pathlib import Path
for filename in Path.home().glob('*.rxt'):
    #os.unlink(filename)  # Para ver los próximos archivos a eliminar, se comenta esta línea. Si estamos seguros, entonces se descomenta.
    print(filename)

Borrado "seguro"

La idea es enviar los archivos a la papelera de reciclaje, en lugar de borrarlos directamente. Para ellos usamos el método send2trash() del módulo send2trash.
Es un módulo de terceros que debe instalarse con pip install –user send2trash.

>>> import send2trash
>>> baconFile = open('bacon.txt', 'a')   # creates the file
>>> baconFile.write('Bacon is not a vegetable.')
25
>>> baconFile.close()
>>> send2trash.send2trash('bacon.txt')

Recorrer un árbol de directorios

La biblioteca os nos proporciona la función os.walk(string), que usada en conjunto con un bucle for (en lugar de range()), en cada iteración se mueve por los directorios del árbol, lo que permite recorrer el arbol de directorios:

En cada iteración del bucle for, la función walk() devuelve:

'''
C:/
    delicius
        cats
            catnames.txt
            zophie.jpg
        walnut
            waffles
                butter.txt
        spam.txt
 
'''
import os
 
for folderName, subfolders, filenames in os.walk('C:\\delicious'):
    print('The current folder is ' + folderName)
 
    for subfolder in subfolders:
        print('SUBFOLDER OF ' + folderName + ': ' + subfolder)
 
    for filename in filenames:
        print('FILE INSIDE ' + folderName + ': '+ filename)
 
    print('')
The current folder is C:\delicious
SUBFOLDER OF C:\delicious: cats
SUBFOLDER OF C:\delicious: walnut
FILE INSIDE C:\delicious: spam.txt
 
The current folder is C:\delicious\cats
FILE INSIDE C:\delicious\cats: catnames.txt
FILE INSIDE C:\delicious\cats: zophie.jpg
 
The current folder is C:\delicious\walnut
SUBFOLDER OF C:\delicious\walnut: waffles
 
The current folder is C:\delicious\walnut\waffles
FILE INSIDE C:\delicious\walnut\waffles: butter.txt

Trabajar con archivos comprimidos ZIP

Para trabajar con archivos comprimidos en ZIP, (extensión .zip) usaremo el módulo zipfile.
La forma de trabajar es muy similar, asignando a tipos de datos ZipFile (similares a Path) el archivo comprimido con el que trabajaremos para gestionar sus datos.

Leer ZIP

Para leer archivos comprimidos en ZIP:

  1. Asignamos el archivo a una variable ZipFile con el método ZipFile del módulo zipfile.
    exampleZip = zipfile.ZipFile(Path.wcd()/'ham'/'meat.zip')
  2. Podemos listar el contenido con el método namelist(), que devuelve una lista de strings con las direcciones relativas del contenido.
    exampleZip.namelist()
  3. Podemos obtener información de cualquier achivo comprimido en ZIP con el método getinfo(nombre_archivo). Devuelve un objeto en el que se pueden consultar los atributos file_size y compress_size.
    spamInfo = exampleZip.getinfo('spam.txt')
    spamInfo.file_size
    spamInfo.compress_size
  4. Una vez hemos terminado de leer en el interior del archivo comprimido, lo cerramos con el métoo close().
    exampleZip.close()

Extraer ZIP

Para extraer todo el contenido de un ZIP, se utiliza el método extractall(), al que se le puede pasar un argumento string en el que se define la dirección en la que se desea extraer. Si no, se extraerá en el directorio raíz.

import zipfile, os
from pathlib import Path
 
p = Path.home()
exampleZip = zipfile.ZipFile(p / 'example.zip')
exampleZip.extractall()  # exampleZip.extractall('C:\\delicious') # Para extraerlo en una diección concreta.
exampleZip.close()

Para extraer un archivo concreto se usa el método extract(), en la que el primer argumento es el archivo a extraer, y el segundo (opcional), la dirección en la que se extraerá (si no existe, se crea).

>>>exampleZip.extract('spam.txt')
'C:\\spam.txt'
>>> exampleZip.extract('spam.txt', 'C:\\some\\new\\folders')
'C:\\some\\new\\folders\\spam.txt'
>>> exampleZip.close()

Crear y añadir a ZIP

Para crear un nuevo archivo ZIP, se utiliza el método ZipFile(), al iguar que se hacía para leer de su interior:

Para añadir elementos (archivos y directorios) al ZIP, se usará el método write():

>>> import zipfile
>>> newZip = zipfile.ZipFile('new.zip', 'w')
>>> newZip.write('spam.txt', compress_type=zipfile.ZIP_DEFLATED)
>>> newZip.close()