Mostrando entradas con la etiqueta Directorios. Mostrar todas las entradas
Mostrando entradas con la etiqueta Directorios. Mostrar todas las entradas

martes, 5 de mayo de 2026

DATOS. Directorios y archivos

Subdirectorios

En cuanto un directorio contiene un determinado número de archivos, y éstos se pueden diferenciar por algún criterio, es habitual que el directorio se divida, generándose una estructura de subdirectorios. Un caso concreto de esto son los expedientes SEO. Además de ser una tendencia lógica, que esto se produzca es una medida de la madurez del expediente y de la funcionalidad que éste tiene como recurso de intervención del SEO. Por ello el estudio de la estructura debe ser parte del análisis de los expedientes SEO. Este estudio se puede automatizar mediante script.

Voy a presentar en esta entrada algunos script dirigidos a facilitar la automatización en este nivel, empezando por la identificación del número de subdirectorios que contiene un listado de directorios.



from pathlib import Path
import csv
from collections import Counter

#Función secundaria. Guarda el listado detallado en un archivo CSV.

def crear_csv(datos, ruta_destino):
    nombre_archivo = Path(ruta_destino) / "listado_directorios.csv"
    try:
        with open(nombre_archivo, mode='w', newline='', encoding='utf-8') as f:
            escritor = csv.writer(f)
            escritor.writerow(["Directorio", "Num_Subcarpetas"])
            for fila in datos:
                escritor.writerow([fila['nombre'], fila['subs']])
        print(f"\n[SISTEMA] Archivo guardado en: {nombre_archivo}")
    except Exception as e:
        print(f"\n[ERROR] No se pudo crear el CSV: {e}")

# Función secundaria. Calcula y muestra la frecuencia de subdirectorios.

def generar_sintesis(datos):
    total_directorios = len(datos)
    if total_directorios == 0:
        return
    frecuencias = Counter(d['subs'] for d in datos)
    
    print("\n--- RESUMEN ESTADÍSTICO DE FRECUENCIAS ---")
    for num_subs, cantidad in sorted(frecuencias.items()):
        porcentaje = (cantidad / total_directorios) * 100
        print(f"CARPETAS con {num_subs} subdirectorios : {cantidad} carpetas -- {porcentaje:.2f}% sobre total")

# Función principal: Coordina el listado, el CSV y la estadística.

def procesar_ruta(ruta_analizar, ruta_guardado):
    path_base = Path(ruta_analizar)
    
    if not path_base.exists() or not path_base.is_dir():
        print("La ruta proporcionada no es válida.")
        return

    resultados = []

    print(f"\nListado de directorios en: {path_base.resolve()}\n")
    
    for item in path_base.iterdir():
        if item.is_dir():
            try:
                conteo = sum(1 for sub in item.iterdir() if sub.is_dir())   # Contamos subdirectorios inmediatos
                resultados.append({'nombre': item.name, 'subs': conteo})
                print(f"[DIRECTORIO] {item.name}: {conteo} subcarpetas")	# Visualización solicitada
            except PermissionError:
                continue 
    crear_csv(resultados, ruta_guardado)	 								# Llamada a funciones secundarias
    generar_sintesis(resultados)

# --- LLAMADA A LA FUNCIÓN ---

if __name__ == "__main__":
    # Define aquí tus rutas
    ruta_objetivo = r""      		# Ruta a explorar
    ruta_donde_archivar = r""       # Ruta para guardar el CSV
    
    procesar_ruta(ruta_objetivo, ruta_donde_archivar)


Este script lee un conjunto de directorios y genera un listado CSV en el que se recoge el número de subdirectorios que contiene cada uno. Para ello utiliza dos funciones secundarias y una principal. La funcionalidad de cada una de ellas queda explicada en los comentarios del script.

Este otro script añade al anterior el conteo de todos los archivos que contie el directorio.


  
from pathlib import Path
import csv
from collections import Counter

#Función secundaria. Guarda el listado detallado en un archivo CSV.

def crear_csv(datos, ruta_destino):
    nombre_archivo = Path(ruta_destino) / "listado_completo_directorios.csv"
    try:
        with open(nombre_archivo, mode='w', newline='', encoding='utf-8') as f:
            escritor = csv.writer(f)
            # Añadimos la nueva columna de archivos totales
            escritor.writerow(["Directorio", "Num_Subcarpetas_Directas", "Num_Archivos_Totales"])
            for fila in datos:
                escritor.writerow([fila['nombre'], fila['subs'], fila['archivos']])
        print(f"\n[SISTEMA] Archivo guardado en: {nombre_archivo}")
    except Exception as e:
        print(f"\n[ERROR] No se pudo crear el CSV: {e}")

#Función secundaria. Calcula y muestra la frecuencia de subdirectorios.

def generar_sintesis(datos):
    total_directorios = len(datos)
    if total_directorios == 0:
        return
    frecuencias = Counter(d['subs'] for d in datos)
    print("\n" + "="*50)
    print("--- RESUMEN ESTADÍSTICO DE FRECUENCIAS ---")
    for num_subs, cantidad in sorted(frecuencias.items()):
        porcentaje = (cantidad / total_directorios) * 100
        print(f"CARPETAS con {num_subs} subdirectorios : {cantidad} carpetas -- {porcentaje:.2f}% sobre total")
    print("="*50)

#Función principal. oordina el análisis profundo de la ruta.

def procesar_ruta(ruta_analizar, ruta_guardado):
    path_base = Path(ruta_analizar)
    if not path_base.exists() or not path_base.is_dir():
        print(f"Error: '{ruta_analizar}' no es una ruta válida.")
        return
    resultados = []
    print(f"\nAnalizando contenido en: {path_base.resolve()}\n")
    print(f"{'DIRECTORIO':<25} | {'SUBS':<6} | {'ARCHIVOS (TOTAL)'}")
    print("-" * 60)
    
    for item in path_base.iterdir():
        if item.is_dir():
            try:
                conteo_subs = sum(1 for sub in item.iterdir() if sub.is_dir())	 # 1. Contar subdirectorios directos (primer nivel)
                conteo_archivos = sum(1 for f in item.rglob('*') if f.is_file()) # 2. Contar TODOS los archivos rglob('*')
                resultados.append({
                    'nombre': item.name, 
                    'subs': conteo_subs, 
                    'archivos': conteo_archivos
                })
                print(f"[DIRECTORIO] {item.name:<12}: {conteo_subs:>3} subs | {conteo_archivos:>5} archivos")	# Visualización por CMD 
            except PermissionError:
                print(f"[!] Sin permiso para acceder a: {item.name}")
                continue
    crear_csv(resultados, ruta_guardado)	    								# Ejecución de reportes
    generar_sintesis(resultados)

# --- SECCIÓN DE LLAMADA A LA FUNCIÓN ---

if __name__ == "__main__":
    # Ajusta estas rutas según tu necesidad
    ruta_objetivo = r""      					# Ruta a explorar
    ruta_donde_archivar = r""                   # Ruta para guardar el CSV
    
    procesar_ruta(ruta_objetivo, ruta_donde_archivar)


Finalmente, en este script cambiamos el enfoque y en vez de trabajar con colecciones de directorios lo vamos a hacer con un directorio en concreto, pero incrementando el detalle del estudio de su estructura (subdirectorios) y de su contenido (archivos), generando un informe útil para el estudio estructural del expediente.



from pathlib import Path
import csv
from collections import Counter

#Función secundaria. Guarda el desglose detallado en un archivo CSV.

def crear_csv(datos, ruta_destino):
    nombre_archivo = Path(ruta_destino) / "estructura_analizada.csv"
    try:
        with open(nombre_archivo, mode='w', newline='', encoding='utf-8') as f:
            escritor = csv.writer(f)
            escritor.writerow([
                "Entrada", 
                "Subcarpetas_Directas", 
                "Archivos_Raiz", 
                "Archivos_en_Subdirs", 
                "Total_Archivos"
            ])
            for fila in datos:
                escritor.writerow([
                    fila['nombre'], 
                    fila['subs'], 
                    fila['archivos_raiz'], 
                    fila['archivos_sub'], 
                    fila['total_archivos']
                ])
        print(f"\n[SISTEMA] Reporte CSV generado en: {nombre_archivo}")
    except Exception as e:
        print(f"\n[ERROR] No se pudo crear el CSV: {e}")

# Función secundaria. Muestra la estadística de frecuencias por consola.

def generar_sintesis(datos):
    total_entradas = len(datos)
    if total_entradas == 0:
        return
    frecuencias = Counter(d['subs'] for d in datos
    print("\n" + "="*85)
    print("--- RESUMEN ESTADÍSTICO DE FRECUENCIAS (Basado en Subdirectorios Directos) ---")
    for num_subs, cantidad in sorted(frecuencias.items()):
        porcentaje = (cantidad / total_entradas) * 100
        print(f"ENTRADAS con {num_subs} subdirectorios directos: {cantidad} unidades -- {porcentaje:.2f}%")
    print("="*85)

# Función principal. Analiza la ruta e incluye el conteo del directorio raíz

def procesar_estructura_principal(ruta_objetivo):
    path_base = Path(ruta_objetivo)
    if not path_base.exists() or not path_base.is_dir():
        print(f"\n[ERROR] La ruta '{ruta_objetivo}' no es válida.")
        return
    resultados = []
    print(f"\nExplorando: {path_base.resolve()}")
    header = f"{'ENTRADA/CARPETA':<25} | {'SUBS':<5} | {'RAIZ':<7} | {'EN SUBS':<8} | {'TOTAL'}"
    print(header)
    print("-" * len(header))
    
    # --- PARTE 1: Analizar la propia carpeta raíz introducida ---
    try:
        subs_raiz = sum(1 for x in path_base.iterdir() if x.is_dir())
        archivos_totales_raiz = sum(1 for x in path_base.rglob('*') if x.is_file())
        archivos_directos_raiz = sum(1 for x in path_base.iterdir() if x.is_file())
        resultados.append({						 # Añadimos la entrada de la carpeta principal
            'nombre': f"RAIZ ({path_base.name})",
            'subs': subs_raiz,
            'archivos_raiz': archivos_directos_raiz,
            'archivos_sub': archivos_totales_raiz - archivos_directos_raiz,
            'total_archivos': archivos_totales_raiz
        })
        print(f"{'-- DIRECTORIO RAIZ --':<25} | {subs_raiz:>5} | {archivos_directos_raiz:>7} | {archivos_totales_raiz - archivos_directos_raiz:>8} | {archivos_totales_raiz:>5}")
    except PermissionError:
        print(f"{'RAIZ':<25} | [!] Acceso denegado")

    # --- PARTE 2: Analizar los subdirectorios internos (si los hay) ---
    for item in path_base.iterdir():
        if item.is_dir():
            try:
                conteo_subs = sum(1 for x in item.iterdir() if x.is_dir())
                total_archivos = sum(1 for x in item.rglob('*') if x.is_file())
                if conteo_subs == 0:
                    archivos_raiz = total_archivos
                    archivos_en_subdirs = 0
                else:
                    archivos_raiz = sum(1 for x in item.iterdir() if x.is_file())
                    archivos_en_subdirs = total_archivos - archivos_raiz
                resultados.append({
                    'nombre': item.name, 
                    'subs': conteo_subs, 
                    'archivos_raiz': archivos_raiz,
                    'archivos_sub': archivos_en_subdirs,
                    'total_archivos': total_archivos
                })  
                print(f"{item.name[:25]:<25} | {conteo_subs:>5} | {archivos_raiz:>7} | {archivos_en_subdirs:>8} | {total_archivos:>5}")
            except PermissionError:
                continue
    if resultados:
        crear_csv(resultados, path_base)
        generar_sintesis(resultados)

# --- SEGMENTO DE LLAMADA A LA FUNCIÓN ---

if __name__ == "__main__":
    ruta_input = input("Introduce la ruta del directorio a analizar: ").strip().replace('"', '')
    procesar_estructura_principal(ruta_input)


martes, 31 de marzo de 2026

DATOS. Directorios y archivos

Clasificar directorios

Clasificación en función del número de archivos

El objetivo de este script es dividir un conjunto de directorios en dos bloques (directorios principales) en función de un criterio datos. En este caso concreto el criterio tiene que ver con la extensión de los archivos que contienen los directorios.

El contexto concreto en que surge este script es el análisis de los directorios expedientes SEO, en los se ha considerado pertinente diferenciar (en determinados subconjuntos) entre aquellos directorios que cuentan únicamente con determinado tipo de archivos (los llamados archivos-Procesador por derivar del uso de servicios de procesamiento de texto) del resto de los directorios que contienen al menos un archivo que no pertenece a esa categoría.

Para lo que ahora nos insteresa, lo relevante es que el script, además de realizar la identificación en función de ese criterio, debe generar un directorio y trasladar a él un determinado subconjunto de directorios.

Como en otras ocasiones, este script ha sido desarrollado con ayuda de Gemini-IA según procedimiento que se explica aquí (pendiente).

Paso a continuación a desarrollar el análisis del scritp, empezando por la identificación de las bibliotecas necesarias para su ejecución, que son las dos que se indican en el segmento de código que sigue.



#--- Bibliotecas necesarias --------------------------- 

import os
import shutil 


Ambas bibliotecas se usan para el trabajo con directorios y archivos, lo que indica que será ésta la acción que desarrolla la función que sigue y se analiza a continuación.



#--- Función --------------------------------------------------------------------------------

def clasificar_directorios(ruta_base):

# Definición de las extensiones de inclusión y de los identificadores de los grupos
    ext_permitidas = {'.doc', '.docx', '.odt'}
    
    nombre_p = "GRUPO_PROCESADOR"
    nombre_o = "GRUPO_OTROS"

# Planteamiento de la estructura de directorios para contener los directorios categorizados

    ruta_p = os.path.join(ruta_base, nombre_p)
    ruta_o = os.path.join(ruta_base, nombre_o)
    
    for d in [ruta_p, ruta_o]:		# Crear contenedores
        if not os.path.exists(d):
            os.makedirs(d)

# Obtención de los directorios a evaluar según categorias

    elementos = [f for f in os.listdir(ruta_base) 
                 if os.path.isdir(os.path.join(ruta_base, f)) 
                 and f not in [nombre_p, nombre_o]]

    print(f"--- Iniciando análisis en: {ruta_base} ---")
    
    resumen = {nombre_p: 0, nombre_o: 0}

    for carpeta in elementos:
        ruta_carpeta = os.path.join(ruta_base, carpeta)
        es_puro = True
        tiene_contenido = False
        archivo_intruso = None

        for raiz, _, archivos in os.walk(ruta_carpeta): # Búsqueda en profundidad
            for arc in archivos:
                tiene_contenido = True
                ext = os.path.splitext(arc)[1].lower()
                
                if ext not in ext_permitidas:
                    es_puro = False
                    archivo_intruso = arc # Guardamos el nombre para el log
                    break
            if not es_puro:
                break
        
        # Clasificación final. Si está vacío, no es "puro procesador", va a OTROS.
        if es_puro and tiene_contenido:
            destino_final = ruta_p
            grupo_asignado = nombre_p
            print(f"[ACEPTADO] {carpeta}: Todo es procesador.")
        else:
            destino_final = ruta_o
            grupo_asignado = nombre_o
            motivo = f"Intruso hallado: {archivo_intruso}" if tiene_contenido else "Carpeta vacía"
            print(f"[RECHAZADO] {carpeta}: {motivo}. Enviado a OTROS.")

        # Traslado del directorio (expedientes) al directorio de categorización 
        try:
            shutil.move(ruta_carpeta, os.path.join(destino_final, carpeta))
            resumen[grupo_asignado] += 1
        except Exception as e:
            print(f"[ERROR] No se pudo mover {carpeta}: {e}")

# Resumen final por pantalla ----------------------------------------------

    print("\n" + "="*50)
    print("RESUMEN DE DIRECTORIOS CREADOS")
    print("-" * 50)
    print(f"Detección de 'Sólo Procesador': {resumen[nombre_p]} carpetas.")
    print(f"Detección de 'Otros/Mixtos':    {resumen[nombre_o]} carpetas.")
    print("="*50)


En esta función caben diferenciar dos partes muy dispares en extensión y peso en el procedimiento: los procesos que llevan a la identificación, catergorización y manipulación de los directorios y la información sobre los resultados que se muestra por pantalla. Evidentemente lo fundamental de la función pertenece a la primera parte, aunque es la segunda la que permite al usuario acceder a la información que le resulta relevante.

Como ya dije antes, en ese conjunto de procesos se diferencia, a su vez, varias fases, siendo la primera la identificación de los criterios de categorización, la segunda la creación de los directorios de categorización, la tercera la identificación del grupo de pertenencia de cada directorio-expediente (incluye la búsqueda en profundidad de los archivos cada directorio-expediente) y la cuarta el traslado del directorio al directorio de su grupo de pertenencia.

Como corresponde a esta estrucrtura de script, la tercera y última parte es el scritp de ejecución de la función, que contiene la asignación de contenido (identificación de la ruta de trabajo) ruta = r"Mi_Ruta" y la llamada a la función, incorporando el parámetro que requiere clasificar_directorios(ruta).



#--- Ejecución de la función -----------------------------------------

if __name__ == "__main__":
   
    ruta = r"Mi_Ruta"	 			# Sustituir por tu ruta deseada
    clasificar_directorios(ruta)	# Llamada a función


DATOS. Directorios y archivos

Seleccionar archivos

Criterio: extensiones de archivos categorizadas

El objetivo de esta función es la selección de archivos en función de su extensión; más concretamente realizar el recuento de archivos atendiendo a las categorías establecidas a partir de la tipología documental.

Lo específico del contexto es la diferenciación entre archivos derivados del uso de procesadores de texto y de otras fuentes, como los resultantes de la conversión a .pdf o los creados como hojas de cálculo. Este contexto se ajusta a procesos de análisis documental como los que tienen lugar cuando se estudia la composición de los expedientes de alumnos.

Al igual que en otras ocasiones, pasamos a continuación a realizar el análisis del script, diferenciando sus tres partes y empezando por las bibliotecas necesarias.



#--- Bibilotecas ----------------------------------------------------

import os
import csv
import pandas as pd
import matplotlib.pyplot as plt


Podemos apreciar la presencia de cuatro bibliotecas cuya función indica la funcionalidad esperada para la función: el uso de directorios y archivos mediante os, la creación de una base de datos simple mediante csv, el análisis de datos mediante pandas y la creación de un gráfico estadístico mediante matplotlib.

Pasamos a mostrar la función la cual iniciamos con los datos necesarios para la categorización mediante categorias_ext y la declaración de la lista datos_archivos, necesaria para el recuento que resulte.



#--- Función ----------------------------------------------------------

def analizar_directorio(ruta_objetivo):

    categorias_ext = {
        'archivo-procesador': ['.doc', '.docx', '.odt'],
        'archivo-pdf': ['.pdf'],
        'archivo-HCalc': ['.xls', '.xlsx', '.ods']
    }
    
    datos_archivos = []
    conteo_categorias = {cat: 0 for cat in categorias_ext}
    conteo_categorias['otros'] = 0

# 1. Búsqueda en profundidad (os.walk)
    for raiz, directorios, archivos in os.walk(ruta_objetivo):
        for nombre_archivo in archivos:
            nombre, ext = os.path.splitext(nombre_archivo)
            ext = ext.lower()
            
            categoria = 'otros'
            for cat, extensiones in categorias_ext.items():
                if ext in extensiones:
                    categoria = cat
                    break
            
            if categoria != 'otros':
                datos_archivos.append({
                    'Nombre': nombre_archivo,
                    'Extensión': ext,
                    'Categoría': categoria
                })
                conteo_categorias[categoria] += 1
            else:
                conteo_categorias['otros'] += 1

# 2. Guardar en CSV
    ruta_csv = os.path.join(os.path.dirname(__file__), 'analisis_archivos.csv')
    with open(ruta_csv, 'w', newline='', encoding='utf-8') as f:
        escritor = csv.DictWriter(f, fieldnames=['Nombre', 'Extensión', 'Categoría'])
        escritor.writeheader()
        escritor.writerows(datos_archivos)

# 3. Mostrar listado por consola
    print(f"{'NOMBRE':<50} | {'CATEGORÍA'}")
    print("-" * 70)
    for fila in datos_archivos:
        print(f"{fila['Nombre'][:48]:<50} | {fila['Categoría']}")

# 4. Tabla resumen con Pandas
    df_resumen = pd.DataFrame(list(conteo_categorias.items()), columns=['Tipo', 'Cantidad'])
    # Solo mostramos las categorías que nos interesan (excluyendo 'otros' si prefieres)
    df_resumen = df_resumen[df_resumen['Tipo'] != 'otros']
    
    print("\n--- RESUMEN DE DATOS ---")
    print(df_resumen.to_string(index=False))

# 5. Gráfico Sectorial
    if df_resumen['Cantidad'].sum() > 0:
        plt.figure(figsize=(8, 6))
        plt.pie(df_resumen['Cantidad'], labels=df_resumen['Tipo'], autopct='%1.1f%%', startangle=140)
        plt.title('Distribución de Archivos por Categoría')
        plt.show()
    else:
        print("\nNo se encontraron archivos de las categorías especificadas para generar el gráfico.")


Lo que sigue en la función son las diferentes partes o fases de su desarrollo, que se relacionan, cada una de ellas, con las bibliotecas vistas en la primera parte del análisis, incluyendo al archivo del documento csv y la visualización por consola de la tabla de datos y del gráfico resultante del uso de matplotlib.

Aunque el almacenamoento y la visualización son interesantes, la razón de ser de la función se desarrolla en la identificada como # 1. Búsqueda en profundidad (os.walk), ya que es en ella donde tiene lugar la búsqueda de los archivos, la identificación diferenciada del nombre y de la extensión y su almacenamiento en función de la categoría que deriva de ésta.

Finalizamos el análisis con el del script de ejecución de la función, que se simplifica al máximo al constar únicamente con el procedimiento para concretar la ruta en la que realizar el análisis ruta = r"Mi_Ruta" y la llamada a la función, incluyendo el parámetro resultante de lo anterior analizar_directorio(ruta)



# --- Script de ejecución de la función ----------------------------------------------------------------------

ruta = r"Mi_Ruta" #Indica aquí tu ruta absoluta
analizar_directorio(ruta)


Todo lo que resulta del uso del script corre a cargo de la función analizar_directorio()

Datos. Directorios y archivos

Seleccionar directorios

El objetivo de esta entrada es crear un script que permita seleccionar directorios cuyo contenido cumpla determinada característica y crear con ellos un directorio de orden superior para almacenar dichos directorios.

El contexto concreto que se plantea como referencia es la búsqueda de los directorios que contengan archivos cuyo nombre contenga la secuencia "SISE". Esto remite al procedimiento explicado en esta entrada. No obstante, el procedimiento que se explica en esta entrada tiene aplicaciones generales cuando la selección de determinado directorio (que es su objetivo principal) obedece a cualquier otro criterio: esté este definido por el tipo de archivo (extensión) o por cualquier criterio relativo al nombre del archivo. También es similar, aunque aquí las diferencias son mayores, a proyectos basados en el número de archivos por directorio.

El análisis del código permite diferenciar tres partes: las bibliotecas necesarias, la o las funciones y el script de llamada y activación de la (o las) función. Empezando por la primera...



# --- Bibliotecas necesarias ----------------------------------
	
import os
import shutil
    

Ambas bibliotecas están pensadas para el trabajo con rutas, directorios y estructuras de directorios y archivos y determinan cómo se formula la función que sigue a continuación. Sobre os puedes consultar esta entrada



# --- Función --------------------------------------------------------------------------------------------

def buscar_directorio(ruta_grupo):
    
# 1. Configuración
    patron = "Mi_Termino"	#Escribe aquí la secuencia (letras) que debe contener el nombre del archivo
    nombre_destino = "Mi_Dir_Destino"	#Escribe aquí el nombre del directorio de destino
    ruta_destino = os.path.join(ruta_grupo, nombre_destino)

    if not os.path.exists(ruta_destino):	#Crea el directorio (nombre_destino -> Mi_Dir_Destino)
        os.makedirs(ruta_destino)
        print(f"[INFO] Creado directorio de destino: {nombre_destino}")

# 2. Identificar subdirectorios
    subdirectorios = [f for f in os.listdir(ruta_grupo) 
                     if os.path.isdir(os.path.join(ruta_grupo, f)) 
                     and f != nombre_destino]

    print(f"[INICIO] Analizando {len(subdirectorios)} carpetas ...")

    conteo = 0

    for carpeta in subdirectorios:
        ruta_completa_carpeta = os.path.join(ruta_grupo, carpeta)
        hallado = False
        archivo_trigger = ""

        for raiz, _, archivos in os.walk(ruta_completa_carpeta): # Búsqueda en profundidad del patrón Mi_Termino
            for nombre_fichero in archivos:
                if patron.lower() in nombre_fichero.lower():
                    hallado = True
                    archivo_trigger = nombre_fichero
                    break
            if hallado:
                break

# 3. Traslado a nombre_destino si hay coincidencia
        if hallado:
            try:
                shutil.move(ruta_completa_carpeta, os.path.join(ruta_destino, carpeta))
                conteo += 1
                print(f"[Mi_Termino] Carpeta: '{carpeta}' -> Movida (Archivo: {archivo_trigger})")
            except Exception as e:
                print(f"[ERROR] No se pudo mover {carpeta}: {e}")

# 4. Resumen final (por consola)
    print("\n" + "="*50)
    print("RESUMEN FINAL FASE 3")
    print("-" * 50)
    print(f"Total carpetas analizadas: {len(subdirectorios)}")
    print(f"Total carpetas con 'Mi_Termino': {conteo}")
    print(f"Ubicación: {ruta_destino}")
    print("="*50)


En el código de la función incluyo los comentarios necesarios para comprender su lógica, que resumo ahora: Establecemos primero las condiciones de búsqueda y el identificador del directorio de destino del resultado de la búsqueda. Ejecutamos después (segundo) la búsqueda de los archivos en profundidad en los directorios de la ruta establecida como objetivo y seleccionamos dichos directorios. Trasladamos (tercero) esos directorios a su carpeta-directorio de destino y, finalmente (cuarto) informamos del proceso y de los resultados mediante la consola.

Para finalizar esta exposición, planteo ahora el script que activa la función, pasándola el parámetro que esta necesita para ejecutarse.



#--- Llamada a función -------------------------------------------------------------------------------

if __name__ == "__main__":

    ruta_proc = r"Mi_Ruta"      # Introduce aquí tu ruta absoluta
    buscar_directorio(ruta_proc)


NOTA
Recuerda que para que todo funciones correctamente, debes reconstruir estos tres componentes como un script único.

domingo, 29 de marzo de 2026

DATOS. Directorios y archivos

Unificar la extensión

Hay ocasiones en las que nos interesa unificar el tipo de documentos a fin de simplificar procesos posteriores. En estos casos disponer de un procedimiento que transforma todos los arhcivos a un mismo tipo es una buena solución.

Cuando la procedencia de los archivos es diversa suele suceder que acabamos trbajando con documentos de contenido similar (o igual) pero en diferentes formatos. Un ejemplo de ello son los archivos derivados del uso de procesadores de texto en los que incluso usando el mismo servicio (MSO-Word) podemos generar documentos .doc o documentos .docx. Si a esto añadimos el uso de LO-Writer obtendremos una colección de archivos en tres formatos diferentes: .doc, .docx y .odt. Cierto que LO-Writer permite acceder a todos ellos indistintamente y de forma fiable y segura, pero si queremos automatizar determinados procesos desde Python unificar la tipología de los archivos puede considerarse una media previa cuanto menos conveniente.

El script que sigue permite unificar todos los archivos de una ruta al formato .docx, lo que facilita el posterior tratamiento de todos los archivos con las funciones disponibles en la biblioteca python-docx.




#Bibliotecas necesarias ---------------------------------------------------

import os
import subprocess
import pathlib

#Función -------------------------------------------------------------------

def convertir_y_limpiar(ruta_absoluta):
    # Configuración de ruta
    RUTA_LIBREOFFICE = r'C:\Program Files\LibreOffice\program\soffice.exe'
    
    if not os.path.exists(RUTA_LIBREOFFICE):
        print(f"Error: No se encontró LibreOffice en {RUTA_LIBREOFFICE}")
        return

    extensiones_a_convertir = {'.doc', '.odt'}
    
    if not os.path.exists(ruta_absoluta):
        print(f"Error: La ruta {ruta_absoluta} no existe.")
        return

    print(f"Procesando archivos en: {ruta_absoluta}...\n")
    
    for raiz, dirs, archivos in os.walk(ruta_absoluta):
        for nombre_archivo in archivos:
            ruta_original = pathlib.Path(os.path.join(raiz, nombre_archivo))
            ext = ruta_original.suffix.lower()

            # 1. Ignorar si ya es .docx
            if ext == '.docx':
                continue

            # 2. Procesar solo .doc y .odt
            if ext in extensiones_a_convertir:
                # Definimos cómo se llamaría el nuevo archivo
                ruta_nuevo_docx = ruta_original.with_suffix('.docx')
                
                print(f"Transformando: {nombre_archivo} -> {ruta_nuevo_docx.name}")
                
                try:
                    # Ejecutar conversión
                    resultado = subprocess.run([
                        RUTA_LIBREOFFICE, 
                        '--headless', 
                        '--convert-to', 'docx', 
                        '--outdir', str(ruta_original.parent),
                        str(ruta_original)
                    ], check=True, capture_output=True)

                    # 3. Verificación de seguridad: ¿Existe el nuevo archivo?
                    if ruta_nuevo_docx.exists():
                        # Eliminamos el original solo si el nuevo ya existe
                        os.remove(ruta_original)
                        print(f"   [OK] Convertido y eliminado original.")
                    else:
                        print(f"   [!] Error: No se encontró el archivo convertido {ruta_nuevo_docx.name}")

                except Exception as e:
                    print(f"   [X] Error procesando {nombre_archivo}: {e}")

    print("\n¡Limpieza y conversión completadas!")

#Script de aplicación de función ---------------------------------------------------------------------

if __name__ == "__main__":
    mi_ruta = "Mi_Ruta"
    convertir_y_limpiar(mi_ruta)


Este script convierte archivos de tipo extensiones_a_convertir = {'.doc', '.odt'} a archivos .docx usando LO RUTA_LIBREOFFICE = r'C:\Program Files\LibreOffice\program\soffice.exe'. No es la única opción, pero sí muy recomendable (y gratuita), motivo por el que insisto en el interés que tiene trabajar con LibreOffice también para automatizar procesos con Python.

En este caso el script susituye los archivos originales por los convertidos, por lo que deberás hacer una copia de seguirdad de aquellos si los quieres. Este otro script mantiene los archivos originales y genera un nuevo directorio donde archiva los convertidos



#---Bibliotecas necesarias ------------------------------------------------

import os
import subprocess
import pathlib

#---Función de conversión -------------------------------------------------

def convertir_a_nueva_carpeta(ruta_origen, carpeta_destino):
    # 1. Configuración de rutas
    RUTA_LIBREOFFICE = r'C:\Program Files\LibreOffice\program\soffice.exe'
    
    if not os.path.exists(RUTA_LIBREOFFICE):
        print(f"Error: No se encontró LibreOffice.")
        return

    # 2. Crear la carpeta de destino si no existe
    ruta_destino_abs = pathlib.Path(carpeta_destino).resolve()
    if not ruta_destino_abs.exists():
        os.makedirs(ruta_destino_abs)
        print(f"Carpeta creada: {ruta_destino_abs}")

    extensiones_a_convertir = {'.doc', '.odt'}

    print(f"Leyendo de: {ruta_origen}")
    print(f"Guardando en: {ruta_destino_abs}\n")
    
    for raiz, dirs, archivos in os.walk(ruta_origen):
        for nombre_archivo in archivos:
            ruta_original = pathlib.Path(os.path.join(raiz, nombre_archivo))
            ext = ruta_original.suffix.lower()

            if ext in extensiones_a_convertir:
                print(f"Convertiendo: {nombre_archivo}...")
                
                try:
                    # 3. Ejecutar conversión apuntando al NUEVO directorio
                    subprocess.run([
                        RUTA_LIBREOFFICE, 
                        '--headless', 
                        '--convert-to', 'docx', 
                        '--outdir', str(ruta_destino_abs), # <--- AQUÍ está el cambio clave
                        str(ruta_original)
                    ], check=True, capture_output=True)

                    # 4. Verificación (Opcional, pero recomendada)
                    nuevo_archivo = ruta_destino_abs / (ruta_original.stem + ".docx")
                    if nuevo_archivo.exists():
                        print(f"   [OK] Guardado en destino.")
                    
                except Exception as e:
                    print(f"   [X] Error con {nombre_archivo}: {e}")

    print("\n¡Proceso finalizado! Los archivos originales permanecen intactos.")

# --- Llamada a la función ------------------------------------------------------------

if __name__ == "__main__":
    origen = "Mi_Ruta_Documentos"  # Carpeta donde están tus .doc/.odt
    destino = "Documentos_Convertidos" # Nueva carpeta donde se guardarán
    convertir_a_nueva_carpeta(origen, destino)


La conversión a .docx no es la única posible, y en muchos casos tampoco la más adecuada, siendo .pdf la extensión preferida para realizar determinadas transaciones o el almacenamiento de documentos. Además también disponemos de biblitecas Python para trabajar con estos documentos. Por ello te proporciono un tercer y último script que convierte archivos procedentes de los servicios de procesador de texto a .pdf tomando a LibreOffice como tecnología de base.



#---Bibliotecas necesarias ------------------------------------------------

import os
import subprocess
import pathlib

#---Función de conversión -------------------------------------------------

def generar_pdfs_en_destino(ruta_origen, carpeta_pdf):
    # --- Configuración ---
    RUTA_LIBREOFFICE = r'C:\Program Files\LibreOffice\program\soffice.exe'
    
    if not os.path.exists(RUTA_LIBREOFFICE):
        print(f"Error: LibreOffice no detectado en {RUTA_LIBREOFFICE}")
        return

    # Extensiones que queremos convertir a PDF
    extensiones_admitidas = {'.doc', '.docx', '.odt'}

    # Crear carpeta de destino absoluta
    ruta_salida = pathlib.Path(carpeta_pdf).resolve()
    if not ruta_salida.exists():
        os.makedirs(ruta_salida)
        print(f"Carpeta de salida creada: {ruta_salida}")

    print(f"Iniciando conversión de documentos a PDF...")
    print(f"Origen: {ruta_origen}\n" + "-"*30)

    # --- Procesamiento ---
    for raiz, dirs, archivos in os.walk(ruta_origen):
        for nombre_archivo in archivos:
            ruta_archivo_original = pathlib.Path(os.path.join(raiz, nombre_archivo))
            ext = ruta_archivo_original.suffix.lower()

            if ext in extensiones_admitidas:
                print(f"Procesando: {nombre_archivo}...")
                
                try:
                    # Ejecutar LibreOffice para exportar a PDF
                    subprocess.run([
                        RUTA_LIBREOFFICE,
                        '--headless',
                        '--convert-to', 'pdf',
                        '--outdir', str(ruta_salida),
                        str(ruta_archivo_original)
                    ], check=True, capture_output=True)

                    # Verificación de que el PDF se creó
                    nombre_esperado = ruta_archivo_original.stem + ".pdf"
                    if (ruta_salida / nombre_esperado).exists():
                        print(f"   [OK] PDF generado correctamente.")
                    else:
                        print(f"   [!] Advertencia: No se confirmó la creación de {nombre_esperado}")

                except Exception as e:
                    print(f"   [X] Error al convertir {nombre_archivo}: {e}")

    print("\n" + "-"*30 + "\n¡Proceso completado! Los archivos originales no han sido modificados.")

# --- Ejecución den la función -----------------------------------------------------------------------

if __name__ == "__main__":
    # Define tus rutas aquí
    mi_directorio_original = "Documentos_Trabajo" 
    mi_directorio_pdfs = "Archivo_Exportado_PDF"
    
    generar_pdfs_en_destino(mi_directorio_original, mi_directorio_pdfs)


viernes, 7 de noviembre de 2025

Lenguajes. Python

Acceso a un directorio

Sintetizo en esta entrada los diferentes modos de acceso a directorios y a su contenido que he ido conociendo mientras buscaba solucionar algunos problemas que implicaban el manejo de directorios. Puede que no sean los únicos disponibles, así que si encuentro alguno más realizaré las modificaciones que correspondan en esta entrada. Lo que pretendo es exponer los procedimientos más simples y cómo se concretan mediante código; por ello sólo sirven como punto de partida para el logro de objetivos de mayor complejidad o más específicos que lo que implica este planteamiento genérico.

Además algunos procedimientos que aquí se explica es posible que ya hayan sido expuestos en otras entrada, o que se vuelvan a plantear en entradas posteriores a esta. En parte es inevitable dado el carácter básico y genérico que tienen estos procedimiento. Espero, no obstante, que este esfuerzo de síntesis y por facilitar el acceso a código básico compense el trabajo de hacer esta entrada... y de leerla.

Para acceder a los directorios y a su contenido necesitamos importar dos bibliotecas básicas de Python, las mismas que he comentado en el inicio de esta subsección: la biblioteca os y la biblioteca pathlib. Es por ello que debemos importarlas al inicio del script:



import os
from pathlib import Path


También necesitamos identificar la ruta absoluta del directorio con el que deseamos trabajar, en nuestro caso...


directorio = 'D:/BasesDatosTest'


A partir de aquí expongo cada una de las opciones, empezando por la más básica, la que proporciona la función os.listdir()


lista_dir1 = os.listdir(directorio)
print(lista_dir1)
for elem in lista_dir1:
	print(elem)


La primera respuesta que obtenemos print(lista_dir1) al aplicar esa función lista_dir1 = os.listdir(directorio) es un lista de los elementos presentes en el directorio, tanto documentos como subdirectorios...

['ALBOR_emle.pdf', 'BasesDatosPROLECR', 'EXPEDIENTES', 'ITPA-CA', 'TEA_BASIIa.pdf', 'TEA_BASIIb.pdf', 'TEA_InfoDP3.pdf', 'TEA_Matrices.pdf', 'TEA_MatricesManual.pdf', 'WISC_BaseDatos']

... pero si queremos verla de forma más accesible, podemos utilizar la segunda fórmula, mediante un ciclo que recorre la lista:



for elem in lista_dir1:
	print(elem)
    

... que nos devuelve...

ALBOR_emle.pdf
BasesDatosPROLECR
EXPEDIENTESITPA-CATEA_BASIIa.pdf
TEA_BASIIb.pdf
TEA_InfoDP3.pdf
TEA_Matrices.pdf
TEA_MatricesManual.pdf
WISC_BaseDatos
La segunda forma de listar los elementos que componen un directorio pasa por usar la función os.walk(), y presenta la siguiente sintaxis...


for carpeta,subcarpeta, archivo in os.walk(directorio):
	print(carpeta)
    print(subcarpeta)
    print(archivo)
    

... que devuelve un complejo listado de listas, que a simple vista parece complicado descifrar, pero que con paciencia y trabajo podemos convertir en un sistema funcional de acceso a cualquiera de los diferentes niveles o nodos que contenga el directorio principal. Aquí únicamente hemos los visualizado.
['BasesDatosPROLECR', 'EXPEDIENTES', 'ITPA-CA', 'WISC_BaseDatos']
['ALBOR_emle.pdf', 'TEA_BASIIa.pdf', 'TEA_BASIIb.pdf', 'TEA_InfoDP3.pdf', 'TEA_Matrices.pdf', 'TEA_MatricesManual.pdf']
D:/BasesDatosTest\BasesDatosPROLECR
['Prolec_CtCo', 'prolec_r_Completo']
['PROLECComprensionTextos.fmp12', 'PROLECComprensionTextos2.fmp12', 'PROLECComprensionTextos2b.fmp12',
'PROLECComprensionTextual.fmp12', 'PROLECEstructurasGramaticales.fmp12', 'PROLECEstructurasGramaticales2.fmp12']

Este ejemplo es una mínima muestra, pero permite observar cómo se han identificado:

  • La estructura y composición del directorio principal print(carpeta), su ruta D:/BasesDatosTest y las dos colecciones de componentes que contiene:
    • Los subdirectorios ['BasesDatosPROLECR', 'EXPEDIENTES', 'ITPA-CA', 'WISC_BaseDatos']
    • Y los archivos que se encuentran en el directorio principal ['ALBOR_emle.pdf', 'TEA_BASIIa.pdf', 'TEA_BASIIb.pdf', 'TEA_InfoDP3.pdf', 'TEA_Matrices.pdf', 'TEA_MatricesManual.pdf']
  • Y el segundo nivel, sólo el subdirectorio D:/BasesDatosTest\BasesDatosPROLECR y una mínima parte de lo que contiene.
    • Sus dos subdirectorios secundarios ['Prolec_CtCo', 'prolec_r_Completo']
    • Y la lista de los documentos que se encuentran ubicados directamente en ese mismo nivel ['PROLECComprensionTextos.fmp12', 'PROLECComprensionTextos2.fmp12', 'PROLECComprensionTextos2b.fmp12', 'PROLECComprensionTextual.fmp12', 'PROLECEstructurasGramaticales.fmp12', 'PROLECEstructurasGramaticales2.fmp12']

Esto nos da una idea de cómo funciona esta función, de la complejidad de la información que aporta y del potencial que tiene su uso en proyecto que requieran, por ejemplo, conocer el contenido y la estructura de directorios complejos.

El tercer modo de obtener información sobre la composición de un directorio implica el uso de la función os.scandir()...



with os.scandir(directorio) as archivos:
	for archivo in archivos:
		print(archivo.name)
        

... y que, como vemos, requiere el uso de la estructura with os.scandir(directorio) as archivos:, a partir de la cual accedemos mediante un ciclo que recorre el iterable que resulta de with (archivos -> for archivo in archivos:), gracias al cual obtenemos como resultado...
ALBOR_emle.pdf
BasesDatosPROLECR
EXPEDIENTES
ITPA-CA
TEA_BASIIa.pdf
TEA_BASIIb.pdf
TEA_InfoDP3.pdf
TEA_Matrices.pdf
TEA_MatricesManual.pdf
WISC_BaseDatos

... aparentemente idéntico al que obtenemos con os.listdir() cuando aplicamos un bucle, pero que en realidad contiene otras funcionalidades, ya que con os.scandir() estamos trabajando con objetos, no con cadenas de texto. Es testigo de ello el modo en que se formula la instrucción de impresión print(archivo.name), diferente al modo en que se formula desde os.listdir (print(elem))

De hechoos.scandir() permite obtener respuestas similares a las que proporciona la cuarta y última forma de acceder al directorio. Esta es la única de las vistas que emplea la biblioteca pathlib, concretamente su módulo Path. Y también permite obtener un objeto, con las funcionalidades que esto conlleva, y que ahora no podemos apreciar por lo limitado de nuestro objetivo:



archivos = Path(directorio)
for archivo in archivos.iterdir():
	print(archivo.name)
    

El interés de esta opción no es tanto el resultado (que es el mismo que la anterior: un listado de elementos (subdirectorios y archivos) como la simplicidad con la que se formula. Por ejemplo, ahora no es necesario usar la estructura with ya que el uso de la función archivos = Path(directorio) posibilita identificar los contenidos de for archivo in archivos.iterdir():.

Lenguajes. Python

Módulo pathlib

El módulo pathlib es una biblioteca estándar que permite trabajar con rutas desde el paradigma de programación orientada a objetos (POO) y es compatible con diferentes sistemas operativos. Se presenta como sustituto de os.path y permite crear y manipular directorios y archivos.

Este módulo, que puedes estudiar con detalle desde este enlace presenta las siguientes funcionalidades:

  • Trabaja dentro del paradigma de la POO, por lo que utiliza objetos Path para representar las rutas en lugar de cadenas de texto, como hace el módulo os.path. Esto permite utilizar métodos y atributos para manejarlas.
  • Es independiente de la plataforma, por lo que maneja automáticamente los diferentes separadores de ruta (\ en Windows y / en Linux/macOS), facilitando que el código funcione correctamente con independencia del sistema operativo.
  • Permite realizar diversas operaciones directamente sobre los directorios (considerado un objeto para Path): crear, eliminar, renombrar o mover archivos y directorios.
  • Facilita el acceso a componentes de la ruta: nombre del archivo (.name), nombre sin extensión (.stem) o sólo la extensión(.suffix).
  • Posibilita saber si una ruta existe, y si se trata de un archivo o un directorio.
  • Permite recorrer directorios y subdirectorios, filtrando los objetos mediante patrones.

miércoles, 5 de noviembre de 2025

Datos. Directorios y archivos

Archivos según extensión

Seguimos trabajando en cómo acceder a datos cuando éstos son los componentes de un directorio, esto es: cómo acceder a archivos. Las formas más sencillas las vimos en entrada anterior, y seguiré recurriendo a su contenido para la solución a los problemas que nos planeamos ahora.

Para que nos hagamos una idea de qué estoy tratando de decir, cuando recopilamos los archivos de nuestros directorios para elaborar la memoria de trabajo (por ejemplo) estamos empleando "manualmente" procedimientos similares, aunque manualmente; ahora el objetivo es automatizarlos. Si al final de curso has tenido que contar los archivos de una serie de directorios como medio para el recuento de actuaciones, seguro que comprenderás perfectamente que esta automatización merece la pena.

Ese trabajo "manual" de recuento de documentos se produce a veces por falta de previsión, instrumentos y/o de sistematicidad en el uso de sistemas de recogida de datos (posiblemente este es el caso del trabajo a desarrollar en el caso de la Memoria), pero otras veces se debe a la propia naturaleza de determinado planteamiento de trabajo. Y dándole una vuelta a estas cuestiones, también es posible que la carencia de recursos para automatizar estas tareas sea, en parte, responsable de que no se realicen más análisis de las actuciones de lso SEO como medio de investigación en la acción, a pesar del gran interés que para la mejora de dicha actuación tienen estos estudios.

Favorece el planeamiento de esta entrada que nos situemos en un supuesto práctico como puede ser el análisis del contenido de los archivos de un conjunto de expedientes SEO con determinado objetivo como razón de ser de ese recuento, pero que requiere cuantificar los archivos por directorio y de ese total cuantos en función de su extensión (o tipo).

Veamos cuáles son los problemas a resolver...

  • Primero: acceder al directorio raíz que contiene los (sub)directorios a estudiar
  • Segundo: acceder sucesivamente a cada uno de esos (sub)directorios
  • Tercero: contar el número de elementos (subdirectorios y documentos) que contiene cada directorio
  • Cuarto: de entre ellos, seleccionar los directorios que contengan un número determinados de elementos (vg. uno).
  • Quinto: diferenciar entre documentos y subdirectorios y trabajar sólo con los directorios que contengan un documento (no un subdirectorio)
  • Sexto: identificar el tipo de documento de que se trata en función de su extensión.
  • Séptimo: contar cada tipo de documento y realizar cálculos con los datos resultantes.

... y cual es el script que los soluciona:



import os

directorio = "D:/EXPEDIENTES_SEO"
contenido = os.listdir(directorio)

n_doc = 0
n_conten = 0
n_dir = 0

#Extensiones a identificar
exten_doc = 0
exten_pdf = 0
doc_texto = 0
exten_xls = 0

#Recorrido del listado de archivos
for exped in contenido:
	dir_exped = directorio + "/" + exped
    
#Seleccionar directorios de un único elemento    
	n_conten = len(os.listdir(dir_exped))
	if n_conten == 1:
		with os.scandir(dir_exped) as entradas:
			for entrada in entradas:
				if entrada.is_file():            
					nombre_sin_extension, extension = os.path.splitext(entrada)            
					if extension == '.doc' or extension == '.docx' or extension == '.odt':            
						exten_doc = exten_doc + 1
					elif extension == '.pdf':
						exten_pdf = exten_pdf + 1                    
					elif extension == '.xls' or extension == '.xlsx' or extension == '.ods':                        
						exten_xls = exten_xls + 1

#Resultados cuantitativos
doc_texto = exten_doc + exten_pdf

print("Número de documentos Procesador de texto " ,str(exten_doc))
print("Número de documentos de tipo PDF " ,str(exten_pdf))                        
print("Total documentos de texto: " , str(doc_texto))
print("Número de documentos Hoja de cálculo " ,str(exten_xls))


La solución a la primera cuestión es bien simple directorio = "D:/EXPEDIENTES_SEO", ya que se basa en expresarla como cadena de texto y asignarla a una variable que usaremos posteriormente para construir la ruta en la que debe buscar el script. A partir de aquí necesitamos trabajar con el módulo import os, ya que es la base de este script. Sin ir mas lejos, de la solución a la segunda cuestión: la creación de un listado de los directorios contenido = os.listdir(directorio) a los que accedemos dir_exped = directorio + "/" + exped mediante for exped in contenido:.

La tercera (contar los elementos del directorio) se resuelve aplicando n_conten = len(os.listdir(dir_exped)) y la cuarta (selección de los directorios que cumplen la condición) mediante if n_conten == 1: que encuadra la mayor parte de los procesos que restan y que son posibles gracias a la estructura with os.scandir(dir_exped) as entradas: a partir de laque se desarrolla un nuevo bucle (for entrada in entradas:) y un nuevo condicional (if entrada.is_file():) que hacen posible dar respuesta al quinto problema (nombre_sin_extension, extension = os.path.splitext(entrada)) gracias a la función zcode>os.path.splitext() y finalmente también al sexto, en este caso mediante sucesivas estructuras condicionales (vg. if extension == '.doc' or extension == '.docx' or extension == '.odt':) que actúan sobre contadores (vg.exten_doc = exten_doc + 1).

En realidad, lo que resta (séptima cuestión) podría considerarse hasta irrelevante en la definición del problema general, ya que son posibles múltiples soluciones. Aquí queda resuelta a modo de informe simple (vg. print("Número de documentos Procesador de texto " ,str(exten_doc))).

lunes, 16 de junio de 2025

Lenguajes. Python

Contenido de un directorio

Supongamos que deseo conocer el contenido de un determinado directorio cuya dirección localizo mediante la funcionalidad Propiedades de Documentos del sistema, que me devuelve D:/PROGRAMACION/OOoBasic/Documentos. Si deseo obtener información sobre el contenido de este directorio, incluyendo los archivos que lo forman y cuántos son éstos, el siguiente script Python me resuelve la tarea:



import os

#Accedo a directorio Documentos
os.chdir('D:/PROGRAMACION/OOoBasic/Documentos')
directorio_actual = os.getcwd()
print(f"Estoy en el directorio: {directorio_actual}")

#Listo los componentes del directorio
lista_archivos = os.listdir('.')

#... y cuento cuántos son
i = 0
for lista in lista_archivos:
	print(lista)
  	i = i + 1
print("Número total de archivos " + str(i))


Tras importar el módulo import os, me posiciono en el directorio mediante la función chdir() y confirmo mi posición mediante la función os.getcwd(). Ahora accedo al contenido del directorio mediante listdir('.') y, tomando como referencia la lista resultante lista_archivos = os.listdir('.'), la uso como iterable en for lista in lista_archivos: y aprovecho para listar los archivos print(lista) y para construir el sumatorio i = i + 1 que devuelve el número de elementos del directorio de trabajo print("Número total de archivos " + str(i)).

domingo, 15 de junio de 2025

Python. Directorios.


Trabajo con directorios


Hemos visto en [Archivosalgunos de los usos que podemos hacer el módulo os y de otros de funcionalidad similar. Nos corresponde ahora plantear un procedimiento simple de uso combinado de algunas de sus funciones; aquellas que nos permitan hacer lo que necesitamos resolver con frecuencia: el posicionamiento y la creación de directorios como paso previo para la creación de documentos y el archivo controlado de los mismos.


En este caso vamos a plantear la realización de tareas previas de posicionamiento y creación de directorios mediante Python, motivo por el cual planteo como marco de trabajo la ubicación del script en el directorio escritorio y el desarrollo de los procedimientos de trabajo dentro de este directorio.

import os

# Obtener el directorio de trabajo actual
directorio_actual = os.getcwd()
print(f"Directorio actual: {directorio_actual}")

# Cambiar al directorio 'ejemplos' dentro del directorio actual
os.chdir('ejemplos')
# Obtener y mostrar el nuevo directorio de trabajo
nuevo_directorio = os.getcwd()
print(f"Nuevo directorio: {nuevo_directorio}")

# Volver al directorio anterior
os.chdir('..')
# Obtener y mostrar el directorio de trabajo después de volver
directorio_anterior = os.getcwd()
print(f"Directorio después de volver: {directorio_anterior}") 
 
#Nos posicionarnos en una ruta de directorio
os.chdir('C:/Users/alons/OneDrive/Escritorio/LECTURA')
print(os.getcwd()) 

Digo procedimientos en plural porque la idea es mostrar tres procedimientos simples. El primero, el anterior, muestra el modo en que resolvemos cuestiones relativas a nuestro posicionamiento dentro de directorios. Los dos que seguirán nos mostrarán procedimientos de creación de directorios. En cualquier caso, todos ellos inician con la instrucción import os, que es requisito previo, dado que todas las instrucciones son funciones de este módulo.

Pero veamos qué hacemos en el primero:

  • Partimos de identificar en qué directorios nos encontramos (desde qué directorio estamos trabajando)...
  • para lo que empleamos la función os.getcwd()a la cual vamos a recurrir cada vez que deseemos comprobar el resultado de nuestro desplazamiento por los directorios...
  • cosa que hacemos mediante la función os.chdir()la cual admite como parámetro
    • un subdirectorio del directorio actual (vg. os.chdir('ejemplos')),
    • un manejador simple de posiciones (vg os.chdir('..'), que nos posiciona en el directorio inmediatamente superior al que nos encontramos)
    • o una cadena de caracteres que expresa una dirección completa (vg. os.chdir('C:/Users/alons/OneDrive/Escritorio/LECTURA'))
También podemos crear un directorio mediante la función mkdir() en la posición o ruta en la que nos encontremos...

import os

# Crear un directorio simple
try:
    os.mkdir("mi_directorio")
    print("Directorio 'mi_directorio' creado.")
except FileExistsError:
    print("El directorio ya existe.")

... siendo posible controlar el correcto funcionamiento del script mediante el control de excepciones (try...except).

Pero en ocasiones (pronto veremos una) necesitamos automatizar la creación de un sistema de directorios anidados, cosa que podemos hacer recurriendo a la función anterior. Para esta tarea resulta mucho más adecuado hacer uso de la función makedirs(), como muestro en este script.

import os

# Crear un directorio anidado
try:
    os.makedirs("directorio_sup/directorio_med/directorio_inf")
    print("Estructura de directorios anidados creada.")
except FileExistsError:
    print("La estructura de directorios ya existe.")

Si te fijas, mientras mkdir() recibe como parámetro el nombre del directorio a crear (os.mkdir("mi_directorio")), makedirs() recibe una cadena compleja  (os.makedirs("directorio_sup/directorio_med/directorio_inf")). 

Pero ambas funciones comparten la conveniencia de usar un controlador de errores o excepciones (try except) y un comportamiento que puede que se nos pase por alto: haber creado el directorio o el sistema de directorios no implica que nos encontremos dentro de él o en uno de sus niveles; de hacho seguimos estando (seguimos posicionados) en el directorio desde el que los creamos, tal y como nos muestra (de nuevo) el uso de la función os.getcwd(). Si deseamos cambiar de posición deberemos recurrir, como antes explicamos, a la función os.chdir().

viernes, 13 de junio de 2025

Python. Directorios.

 Operaciones con archivos


Trabajar con archivos es tan interesante como el trabajo con directorios, así que dedicamos esta entrada a mostrar algunas de las funciones os que nos permiten obtener información sobre archivos y operar con ellos.


En primer lugar vamos a obtener información relevante sobre un determinado archivo, esto es, algunos de sus metadatos. Para ello crearé un script a partir del ejemplo que muestra [certidevs].

import os

# Posicionamiento en el directorio
os.chdir('D:')
#Identificación confirmatoria como directorio activo
directorio = os.getcwd()
print(f"Ahora estamos en el  directorio: {directorio}")

#Identificación del archivo objeto de análisis 

archivo = 'archivo.pdf'

#Información sobre el nombre del archivo y sobre su existencia
print(f"Vamos a trabajar con el archivo: {archivo}")
print(f"Confirmación de existencia del archivo: {os.path.exists(archivo)}")

#Condicinalidad básica: de existir el archivo (metadatos) (if) y mensaje alternativo (else

if os.path.exists(archivo):
# Obtener el tamaño del archivo
    tamaño = os.path.getsize(archivo)
    print(f"Tamaño del archivo: {tamaño} bytes")  
# Obtener la última modificación (timestamp)
    tiempo_mod = os.path.getmtime(archivo)
# Convertir timestamp a fecha legible
    import datetime
    fecha_mod = datetime.datetime.fromtimestamp(tiempo_mod)
    print(f"Última modificación del archivo: {fecha_mod}")
# Comprobación de permisos
    es_legible = os.access(archivo, os.R_OK)
    es_escribible = os.access(archivo, os.W_OK)
    es_ejecutable = os.access(archivo, os.X_OK)
  print(f"Permisos sobre el archivo - Lectura: {es_legible}, Escritura: {es_escribible}, Ejecución: {es_ejecutable}")

else:
    print ("El archivo NO EXISTE")

En primer lugar importamos el módulo (import os) (1) y nos posicionamos en el directorio donde se ubica el archivo (os.chdir('D:')). Después realizamos varias comprobaciones e informaciones sobre directorio y archivo. Ahora nos interesa especialmente comprobar la existencia del archivo (print(f"Confirmación de existencia del archivo: {os.path.exists(archivo)}")), aunque, como veremos después, ya hemos dispuesto medidas dentro de la opcionalidad posterior (else).

Tras estas comprobaciones desarrollamos una estructura condicional (if os.path.exists(archivo):) (2) dentro de la cual obtenemos información sobre el tamaño del archivo (os.path.getsize()), la última  modificación del mismo (os.path.getmtime()) y una serie de datos sobre permisos (os.access()) (3

Mención especial requiere el procedimiento de conversión del metadato de fecha (de última modificación) (os.path.getmtime(archivo)) en una fecha legible para el usuario. Para ello se requiere recurrir al módulo datetime (import datetime) y a su función de conversión (datetime.datetime.fromtimestamp(tiempo_mod)). Ahora no vamos a tratar sobre este módulo (4), pero dejo indicado un posible tratamiento del mismo en otro momento.

Además de obtener información sobre un archivo, también podemos manipularlo, entendiendo por ello renombrarlo (os.rename()), moverlo a otro directorio (os.replace()) o eliminarlo (os.remove()); y todo ello desde el sistema, sin que sea necesario acceder a él (lectura-escritura). Veamos un sencillo script que da continuidad al anterior (5).

# Renombramos el archivo
os.rename('archivo.pdf', 'archivo_bis.pdf')

# Movemos el archivo
os.replace('archivo_bis.pdf','subdirectorio/archivo.pdf')

# Eliminamos el archivo
os.remove('archivo_copia.pdf')

Obsérvese que la función rename() requiere dos parámetros, bien como string, bien como variables (uno para el nombre antiguo del archivo y otro para el nuevo), al igual que la función replace() (en este caso el primero identifica el archivo a desplazar y el segundo requiere identificar el directorio (que debe existir) y el nombre (que puede ser diferente) del archivo que se desplaza. remove(), por el contrario, sólo requiere identificar el nombre del archivo que se va a eliminar.

NOTAS

(1) En buena lógica (y práctica de programación), ya que después importamos también el módulo datatime (import datetime) deberíamos realizar esta importación en este momento, pero por motivos didácticos la postponemos al momento en que resulta necesaria para la continuidad del script.
(2) Obsérvese que se mantiene el sangrado incluyendo dentro de la condición todas las instrucciones que siguen hasta else
(3) La función access() requiere dos parámetros, el nombre del archivo y la identificación del atributo específico de permiso o acceso. V.g. os.R_OK nos informa si el archivo es legible, esto es, si tiene permisos de lectura.
(4) El módulo datetime es de gran interés para nuestro trabajo dado que nos permite manipular fechas y horas. Información sobre el mismo en [este enlace]
(5) Damos por supuesto que existen los archivos y el directorio que se indican en las instrucciones. Aunque en este script no es imprescindible, en una formulación operativa del mismo el trabajo con el archivo debería condicionarse a la identificación previa de su existencia dentro del directorio en el que estemos trabajando, por lo que debería incluirse dentro de la primera parte del condicional.

jueves, 12 de junio de 2025

Lenguajes. Python

Operaciones con directorios

Una de las utilidades del módulo os es el trabajo con directorios, así que dedicaré esta entrada a las funciones que nos permiten realizar estas operaciones.

Lo primero que vamos a hacer (1) es conocer en qué directorio estamos trabajando, esto es, en qué directorio se encuentra el archivo desde el que trabajamos. Para ello empleamos la función os.getcwd() que nos devuelve la ruta completa del directorio en que nos encontramos (2):



# Obtener el directorio de trabajo actual
directorio_actual = os.getcwd()
print(f"Estás en: {directorio_actual}")


Una vez que sabemos en qué directorio estamos situados, puede interesarnos acceder a otro diferente (3). Para ellos disponemos de la función os.chdir() a la que pasamos como parámetro el string del directorio al que deseamos acceder para, por ejemplo, crear en él un nuevo directorio; para ello empleamos la función os.mkdir(). Esto es lo que hace el siguiente script.



import os

# Obtenemos el directorio de trabajo inicial
directorio_inicial = os.getcwd()
print(f"Estás en: {directorio_inicial}")

# Cambiamos el directorio de trabajo y obtenemos su ruta
os.chdir('D:/ArchivosVarios')
directorio_actual = os.getcwd()
print(f"Y ahora estás en: {directorio_actual}")

# Y finalmente creamos un nuevo directorio en él
os.mkdir('nuevo_directorio')


También podemos eliminar un directorio, tanto si está vacío os.rmdir() como si no lo está, aunque para esto deberemos importar el módulo shutil, que debe ser usado con precaución (4).



# Módulo os. Elimina un directorio vacío
os.rmdir('nuevo_directorio')

# Módulo complementario para operaciones de archivos
import shutil

# ¡Cuidado! Elimina todo el contenido
shutil.rmtree('directorio_con_contenido')


Una vez que accedemos a un directorio nos puede interesar conocer qué subdirectorios y archivos contiene (listdir()), sólo qué archivos (isfile) o sólo qué directorios (isdir())...



#Ahora listamos los subdirectorios y archivos del directorio actual

lista_conten= os.listdir('.')	# '.' representa el directorio actual
print(f"Contenido del directorio: {lista_conten}") 

#Sólo mostramos los archivos

archivos = [f for f in lista_conten if os.path.isfile(f)]
print(f"Archivos: {archivos}")	

#Sólo mostramos los subdirectorios

directorios = [d for d in lista_conten if os.path.isdir(d)]
print(f"Directorios: {directorios}")


... aunque es posible que deseemos hacer un listado con información más detallada



for elemento in lista_conten:
	tipo = "Directorio" if os.path.isdir(elemento) else "Archivo"
    tamaño = os.path.getsize(elemento)	# Tamaño en bytes
    print(f"{elemento} - {tipo} - {tamaño} bytes")


Notas:


1Realmente lo primero que haremos será importar el módulo os mediante la instrucción correspondiente (import os), pero esto lo damos por sabido.
2La segunda instrucción es la que nos devuelve por pantalla esa ruta. En otras ocasiones puede interesarnos conocerla para trabajar con ella, de ahí el interés por asociarla a una variable.
3La función getcwd() interesa para conocer desde donde se ejecuta nuestro script, pero necesitamos la función chdir() para navegar por el sistema de archivos.
4El módulo shutil permite realizar operaciones de alto nivel con archivos y colecciones de archivos, incluyendo la copia y el borrado de archivos y directorios. Su uso debe ser cuidadoso, ya que podemos eliminar accidentalmente archivos que no deseamos borrar.

Python. Directorios.

Módulo os

Aunque son varios los módulos Python para trabajar con el sistema operativo, iniicalmente vamos a trabajar con este módulo por su universalidad y por las funcionalidades que ofrece. El módulo os está preinstalado, por lo que no es necesario instalarlo mediante pip install, pero sí importarlo al inicio del script mediante import os. A partir de este momento tenemos a nuestra disposición...

  • Obtener información del sistema operativo
  • Acceder a variables del entorno del sistema
  • Trabajar con rutas y directorios
  • Listar directorios y archivos
  • Ejecutar comandos del sistema
  • Obtener información sobre metadatos de archivos
  • Realizar operaciones con archivos
  • Gestionar procesos del sistema operativo

El módulo os actúa como intermediario entre Python y el sistema operativo con el que trabajemos, facilitando la ejecución de operaciones que requieren comandos específicos, por lo que es fundamental para automatizar acciones que requieren interactuar con el sistema operativo.

En esta entrada y en las siguientes trataremos sobre las operaciones antes listadas que nos sean de utilidad para nuestros objetivos. Conforme las necesitemos para automatizar procesos, iré ampliando esta subsección del blog. Por ahora empezaré con las funciones que nos aportan información sobre el sistema y sobre sus variables.



import os

#Informa sobre qué sistema operativo se está usando
print(os.name)	#Devuelve nt para windows

#os.environ permite acceder a todas la variables de entorno
usuario = os.environ.get('USERPROFILE')	#Permite obtener el valor de una variable
print(usuario)

os.environ['MI_VARIABLE'] = 'valor'	# Permite establecer una nueva variable

for key, value in os.environ.items():	#Lista todas las variables de entorno
	print(f"{key}: {value}")
    

Notas

1Para más información sobre os ver esta web. Además de os disponemos de otros módulos para trabajar con el sistema operativo: pathlib, sys, subprocess, io, y shutil. Con independencia de que sean tratados en entradas específicas, en la actual y en las que siguen hablaremos de aquellos que resulten pertinentes para determinadas operaciones.

2El módulo os ofrece una amplia gama de funciones para trabajar con el sistema operativo, con independencia de cuál sea éste. Para el desarrollo de esta entrada me basaré en certidevs, web muy interesante y didáctica.