Archivos, carpetas y rutas: leer datos sin depender de tu escritorio
«El archivo está al lado del script». Una afirmación tranquilizadora hasta que el programa lo busca en otro sitio. El archivo no se ha movido; ha cambiado la carpeta desde la que se ejecuta el proceso. Podría dedicar mi extraordinaria capacidad a tareas más vistosas que localizar un texto, pero alguien ha confiado la organización de una entrega al lugar desde el que hizo doble clic. Vamos a resolverlo antes de declarar desaparecida una carpeta perfectamente existente.
Una ruta indica dónde buscar un archivo. Para que una lectura sea repetible necesitas decidir desde qué base se interpreta, construirla sin pegar separadores a mano y distinguir que no exista de que no puedas abrirlo. Prepararemos un lector pequeño de una entrega ficticia en Python y C#. No interpreta datos BIM: deja resuelta una dependencia anterior, que el programa lea el archivo que tú has elegido y pueda explicar por qué no lo ha conseguido.
La carpeta del programa y la carpeta de trabajo son cosas distintas
La carpeta de trabajo es la base que utiliza un proceso para interpretar sus rutas relativas. Si ejecutas una orden desde una terminal, normalmente será la carpeta en la que estás en ese momento. Un editor, una tarea programada o una aplicación que lanza otro proceso pueden establecer una diferente. Por eso una lectura con "puertas.txt" no significa necesariamente «el archivo que acompaña a este script». Significa buscar ese nombre respecto a una base que conviene conocer.
Una ruta absoluta identifica una ubicación desde la raíz correspondiente, como una unidad de Windows o la raíz de un sistema Unix. Una relativa necesita una base: Entrega final/puertas.txt dice qué recorrido seguir, pero todavía falta saber desde dónde. El punto . representa la carpeta actual; .., la superior. Ninguno recuerda por cortesía dónde guardaste el proyecto. Me veo obligado a precisar esto porque al sistema de archivos no le han instalado nuestra notable capacidad para dar contexto por supuesto.
Puedes observar el problema sin mover archivos: cambia la carpeta de trabajo y repite una lectura relativa. Si ahora no encuentra la entrada, la primera pregunta es qué ruta completa está intentando abrir. Añadir copias de puertas.txt en cada carpeta hasta que alguna funcione dificulta la investigación: puedes terminar leyendo una versión antigua y obteniendo resultados muy convincentes sobre la entrega equivocada.
En una herramienta de revisión, yo haría visible esa ruta antes de mostrar los resultados. No para inundar a la persona usuaria con detalles, sino para que pueda comprobar qué entrega ha seleccionado. Un «procesado correctamente» sin una entrada identificable tiene una confianza en sí mismo que los datos rara vez justifican.
Elige una base que tenga sentido para el dato que vas a leer
Hay dos necesidades habituales. Un recurso que acompaña al código puede localizarse respecto a la ubicación del programa. Una entrega elegida por la persona usuaria debería recibirse como configuración o argumento, sin obligarla a colocar sus archivos junto al ejecutable. En el ejemplo elegiremos esta segunda opción: le pasaremos una carpeta base y dentro buscaremos Entrega final/puertas.txt.
Eso evita guardar una ruta como C:/Users/mi_usuario/Desktop/... dentro de la lógica. Esa ubicación puede servir como argumento en una prueba personal, pero convertirla en requisito del programa obliga a que los demás imiten tu escritorio. Una forma bastante discreta de transformar un script compartido en una biografía del ordenador de su autor.
Construye el recorrido con las operaciones de rutas del lenguaje. En Python, Path permite unir segmentos mediante /; en C#, Path.Combine hace esa unión. No necesitas decidir cuántas barras añadir al final de una carpeta ni corregir dobles separadores con sustituciones de texto. Conserva el nombre como nombre: el espacio de Entrega final es válido. En la terminal, las comillas sirven para pasar una ruta con espacios como un solo argumento.
Pasar una base no establece por sí solo una barrera de seguridad. Resolver una ruta o combinar sus partes no demuestra que la persona tenga permiso para leerla, ni restringe automáticamente todas las entradas a una carpeta autorizada. Nuestro nombre de archivo está fijado por el programa; una aplicación que acepte rutas arbitrarias y las abra para otras personas necesita controles adicionales. Aquí vamos a comprobar una lectura local, no a construir un servicio de descarga.
Un lector Python que muestra qué archivo está utilizando
Crea una carpeta para el ejercicio y, dentro, otra llamada Entrega final. Guarda en ella puertas.txt, como texto UTF-8, con el siguiente contenido. Puedes utilizar un editor de texto; no hace falta abrir una hoja de cálculo para escribir dos líneas. La tilde nos ayudará a comprobar que estamos leyendo texto, además de acertar con la ubicación.
Guarda leer_entrega.py en la carpeta del ejercicio, al mismo nivel que Entrega final. Este programa recibe una carpeta base. expanduser() permite interpretar una referencia inicial al directorio personal con ~; resolve() obtiene una ruta absoluta y resuelve enlaces simbólicos según el sistema. No crea el archivo ni concede acceso. Después unimos los dos segmentos de la entrega y mostramos la ubicación que vamos a abrir.
Abre una terminal en la carpeta del ejercicio y ejecuta esta orden. El argumento "." significa que esa carpeta será la base. La primera línea de salida debe señalar Entrega final/puertas.txt, con los separadores que use tu sistema; a continuación aparecerán las dos líneas del archivo. El código de salida cero indica que esta lectura terminó correctamente, no que los códigos de una entrega hayan pasado una validación.
Si necesitas lanzar el programa desde otro sitio, puedes pasar una base absoluta o una relativa que tenga sentido desde esa nueva carpeta. Por ejemplo, crea una subcarpeta trabajo vacía, abre allí la terminal y ejecuta la siguiente orden. Ahora .. identifica la carpeta del ejercicio; el programa mostrará la misma ruta de entrada que antes. Cambian el punto de partida y el argumento, pero no el archivo seleccionado.
Para un recurso que deba acompañar al script, otra decisión posible es usar Path(__file__).resolve().parent como base. Eso cambia el criterio: lo vincula a la ubicación del archivo Python. No lo aplicaría automáticamente a entregas externas, y __file__ no está disponible en todos los contextos interactivos. La referencia de pathlib de Python 3.12 documenta estas operaciones; lo que debes decidir tú es qué ubicación representa el dato.
La misma base explícita en un programa C#
C# también permite resolver una ruta relativa antes de abrir el archivo. Path.GetFullPath obtiene la ruta completa respecto a la carpeta de trabajo cuando la entrada es relativa; a diferencia de resolve() en Python, no estamos utilizándolo para resolver físicamente enlaces simbólicos. No necesitas atribuir a las dos funciones un comportamiento idéntico para aplicar el mismo criterio: recibir una base, construir el nombre esperado y mostrar qué se va a leer.
Desde la carpeta del ejercicio, con el SDK de .NET 8 instalado, crea un proyecto de consola con esta orden. Se generará una subcarpeta Lector; conserva Entrega final donde está. Después sustituye el Program.cs generado por el bloque siguiente. El programa recibirá la base como argumento, por lo que no necesita copiar la entrega al directorio de compilación.
La lectura utiliza UTF-8 con detección de secuencias inválidas. File.ReadAllText puede reconocer marcas de codificación al principio del archivo; este ejemplo se prepara como UTF-8 y no es un detector de cualquier formato de texto. Los errores de archivo ausente, carpeta ausente, acceso y descodificación se distinguen en el arranque. Otros problemas de entrada o de E/S no se convierten silenciosamente en un informe vacío.
Ejecuta la orden siguiente desde la carpeta del ejercicio. --project indica dónde está el proyecto; el argumento después de -- llega a nuestra aplicación. Especificar dónde está el proyecto no convierte esa ubicación en la base de todos sus datos. El punto sigue representando la carpeta de trabajo del proceso, que aquí hemos elegido al abrir la terminal.
La salida debe identificar el mismo archivo y conservar las tildes. En una aplicación compilada, AppContext.BaseDirectory sirve para ubicar la base de ejecución, pero no equivale necesariamente a la carpeta del proyecto: durante el desarrollo suele estar dentro de la salida de compilación. Si eliges esa base para recursos propios, tendrás que preparar su distribución. Para la entrega del usuario, mantener el argumento explícito evita esa dependencia. La documentación de lectura de archivos de .NET 8 detalla la operación y las excepciones.
Comprueba el fallo que tienes, antes de corregir otro distinto
Renombra temporalmente puertas.txt y repite la lectura. Debe aparecer el mensaje de ausencia y una salida de error. Devuélvele el nombre y comprueba que vuelve a leerlo. Es una prueba pequeña, pero obliga a que tu programa distinga entre no encontrar una entrada y haber leído una entrega sin incidencias. No devolvería una lista vacía en ambos casos: ahorra una excepción a cambio de mentir sobre lo sucedido.
El permiso denegado requiere otra comprobación. Un archivo puede existir y seguir sin estar disponible para la cuenta que ejecuta el proceso; también puede estar en una ubicación de red que esa cuenta no alcance. Si te ocurre, comprueba la identidad de ejecución y el permiso necesario. Ejecutar todo como administrador para ver si desaparece el error altera las condiciones de la prueba y amplía lo que el programa puede hacer. Yo empezaría por entender qué lectura estás intentando autorizar, una extravagancia aparentemente menos popular que elevar privilegios.
Los ejemplos intentan abrir el archivo y reaccionan al error de esa operación. Una comprobación previa con exists() o File.Exists() no garantiza que puedas leerlo después: entre ambas operaciones puede cambiar, y la existencia no resuelve el acceso. Mostrar la ruta ayuda al diagnóstico; capturar el fallo de apertura permite decidir qué comunicar. Son tareas distintas, como vimos al separar errores de datos y excepciones del proceso.
Antes de compartir el lector, ejecútalo desde dos carpetas y selecciona la misma entrega de forma explícita. Comprueba también una base con espacios, un archivo ausente y un fallo de acceso en un entorno de prueba. Si tienes que copiar el archivo al escritorio de quien lo desarrolló para que funcione, la lectura todavía tiene un requisito oculto. Resolver una ruta es sencillo; dejar de tratar tu ordenador como parte del contrato es la mejora que permite utilizar el programa en el de otra persona.
Respuestas