{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<p>\n",
    "<font size='5' face='Georgia, Arial'>IIC-2233 Apunte Programación Avanzada</font><br>\n",
    "<font size='1'> Modificado al 2026-1 por Equipo Docente IIC2233. </font>\n",
    "</p>"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Tabla de contenidos\n",
    "\n",
    "1. [Módulo `os`](#Módulo-os)\n",
    "2. [*Paths* y nombres de archivos](#Paths-y-nombres-de-archivos)\n",
    "3. [Problema de portabilidad de *paths*](#Problema-de-portabilidad-de-paths)\n",
    "4. [Navegación entre directorios](#Navegación-entre-directorios)\n",
    "5. [Un ejemplo de lectura y escritura básico](#Un-ejemplo-de-lectura-y-escritura-básico)\n",
    "6. [Pathlib](#Pathlib)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Módulo `os`\n",
    "\n",
    "El módulo [`os`](https://docs.python.org/3/library/os.html) de Python nos provee una interfaz portable para ejecutar operaciones relacionadas al sistema operativo y al sistema de archivos. En lo inmediato lo utilizaremos para ejecutar operaciones sobre las rutas (*path*) de los archivos, utilizando `os.path`. El manejo apropiado de los *paths* usando este módulo nos ayudará a hacer nuestro código más portable, permitiéndonos ejecutar código sin problemas en distintos sistemas operativos."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [],
   "source": [
    "import os"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## *Paths* y nombres de archivos\n",
    "\n",
    "Un *path* (o ruta) nos indica el lugar donde se encuentra un archivo o un directorio dentro de un árbol que representa nuestro sistema de archivos. Como notarás, las carpetas y archivos en un computador se almacenan jerárquicamente: carpetas (o directorios) dentro de otras carpetas, y archivos dentro de carpetas. Un *path* es la dirección de un directorio o archivo dentro de esta jerarquía. La siguiente figura muestra de forma gráfica un ejemplo de esta jerarquía.\n",
    "\n",
    "<center><img src=\"data/files.png\" alt=\"Drawing\" style=\"width: 500px;\"/></center>"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Entonces, un ejemplo de ruta es: `'Pedro/Libros/calculo.pdf'`. Esta especifica la dirección de un archivo `calculo.pdf`, que está dentro del directorio `Libros`, que a su vez está dentro de la carpeta `Pedro`.\n",
    "\n",
    "Es necesario trabajar con *paths* cuando un programa quiere acceder a un archivo para leerlo o para crear un nuevo archivo y guardarlo. A través de los *paths* es que un programa es capaz de especificar a cuál archivo o directorio quiere acceder dentro de un computador."
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Un _path_ puede ser **absoluto** o **relativo**.\n",
    "\n",
    " - Un ***path* absoluto** es la dirección de un archivo o directorio desde la **raíz** del sistema de archivos. Puede pensarse como las instrucciones para llegar desde el comienzo del computador. Un *path* absoluto **siempre** comienza con la dirección del directorio principal o directorio raíz (*root*). Este, en muchos sistemas operativos se representa con el carácter `/` , pero en otros puede variar (en Windows puede ser `\\` o `C:/`). Un *path* absoluto tiene el mismo significado de manera independiente del directorio en donde se está ejecutando el programa. Tiene la ventaja que la ruta no presenta ambigüedades, pero requiere que la ruta exista **exactamente igual** en todos los sistemas de archivos en que se ejecuta el programa.\n",
    " \n",
    " - Un ***path* relativo** **nunca** comienza con el carácter de directorio raíz e indica una dirección **relativa a cierto directorio**. Un *path* relativo se interpreta a partir de algún directorio específico, que corresponde por lo general al que contiene al programa actual. Tiene la ventaja que permite referenciar a un directorio de manera más simple, ya que no recurre a toda la jerarquía del sistema de archivos. Pero aún así requiere cuidado al momento de ejecutar desde un directorio distinto al esperado."
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Por ejemplo, si seguimos el ejemplo de la estructura de la imagen superior, podemos definir rutas para el archivo `python.pdf`:\n",
    "\n",
    "- `\"/Users/Pedro/Libros/python.pdf\"` es la ruta absoluta del archivo, ya que detalla desde el directorio raíz la dirección del archivo. Esta ruta es única, no hay otra ruta absoluta para el archivo.\n",
    "- `\"Users/Pedro/Libros/python.pdf\"` es la ruta relativa del archivo, **relativa desde el directorio raíz `\"/\"`**.\n",
    "- `\"Libros/python.pdf\"` es la ruta relativa del archivo, **relativa desde el directorio `\"/Users/Pedro\"`**.\n",
    "- `\"python.pdf\"` es la ruta relativa del archivo, **relativa desde el directorio `\"/Users/Pedro/Libros\"`**.\n",
    "\n",
    "Es importante notar las sutilezas de ambos tipos de *paths* al crear un programa. Recordemos que al ejecutar un programa escrito en Python, esta **ejecución se realiza en cierto directorio** (o carpeta) del computador. Al usar rutas absolutas, no importa desde qué directorio se ejecute el programa, ya que la dirección del *path* no cambia. A diferencia de las rutas relativas, que sí cambian dependiendo de la carpeta donde se ejecute. Por otro lado, un programa no necesariamente se ejecuta siempre en el mismo equipo (a veces se ejecuta en nuestro computador, o a veces en el computador del ayudante). Es por eso que **usar rutas absolutas dificulta el funcionamiento de un programa, ya que pueden estar fijas a un computador específico**, con una estructura única que no se replica en otros computadores. Por todo lo anterior, no se aconseja escribir programas que usen rutas absolutas para acceder a archivos, sino que **deben utilizar solo rutas relativas**."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [
    {
     "ename": "FileNotFoundError",
     "evalue": "[Errno 2] No such file or directory: '/home/archivo.txt'",
     "output_type": "error",
     "traceback": [
      "\u001b[31m---------------------------------------------------------------------------\u001b[39m",
      "\u001b[31mFileNotFoundError\u001b[39m                         Traceback (most recent call last)",
      "\u001b[36mCell\u001b[39m\u001b[36m \u001b[39m\u001b[32mIn[2]\u001b[39m\u001b[32m, line 5\u001b[39m\n\u001b[32m      1\u001b[39m \u001b[38;5;66;03m# Path absoluto\u001b[39;00m\n\u001b[32m      3\u001b[39m path_absoluto = \u001b[33m'\u001b[39m\u001b[33m/home/archivo.txt\u001b[39m\u001b[33m'\u001b[39m\n\u001b[32m----> \u001b[39m\u001b[32m5\u001b[39m \u001b[38;5;28;01mwith\u001b[39;00m \u001b[38;5;28;43mopen\u001b[39;49m\u001b[43m(\u001b[49m\u001b[43mpath_absoluto\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[33;43m'\u001b[39;49m\u001b[33;43mrt\u001b[39;49m\u001b[33;43m'\u001b[39;49m\u001b[43m)\u001b[49m \u001b[38;5;28;01mas\u001b[39;00m archivo:\n\u001b[32m      6\u001b[39m     lineas = archivo.readlines()\n\u001b[32m      8\u001b[39m lineas\n",
      "\u001b[36mFile \u001b[39m\u001b[32m~/.pyenv/versions/3.12.9/lib/python3.12/site-packages/IPython/core/interactiveshell.py:343\u001b[39m, in \u001b[36m_modified_open\u001b[39m\u001b[34m(file, *args, **kwargs)\u001b[39m\n\u001b[32m    336\u001b[39m \u001b[38;5;28;01mif\u001b[39;00m file \u001b[38;5;129;01min\u001b[39;00m {\u001b[32m0\u001b[39m, \u001b[32m1\u001b[39m, \u001b[32m2\u001b[39m}:\n\u001b[32m    337\u001b[39m     \u001b[38;5;28;01mraise\u001b[39;00m \u001b[38;5;167;01mValueError\u001b[39;00m(\n\u001b[32m    338\u001b[39m         \u001b[33mf\u001b[39m\u001b[33m\"\u001b[39m\u001b[33mIPython won\u001b[39m\u001b[33m'\u001b[39m\u001b[33mt let you open fd=\u001b[39m\u001b[38;5;132;01m{\u001b[39;00mfile\u001b[38;5;132;01m}\u001b[39;00m\u001b[33m by default \u001b[39m\u001b[33m\"\u001b[39m\n\u001b[32m    339\u001b[39m         \u001b[33m\"\u001b[39m\u001b[33mas it is likely to crash IPython. If you know what you are doing, \u001b[39m\u001b[33m\"\u001b[39m\n\u001b[32m    340\u001b[39m         \u001b[33m\"\u001b[39m\u001b[33myou can use builtins\u001b[39m\u001b[33m'\u001b[39m\u001b[33m open.\u001b[39m\u001b[33m\"\u001b[39m\n\u001b[32m    341\u001b[39m     )\n\u001b[32m--> \u001b[39m\u001b[32m343\u001b[39m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[43mio_open\u001b[49m\u001b[43m(\u001b[49m\u001b[43mfile\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43m*\u001b[49m\u001b[43margs\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43m*\u001b[49m\u001b[43m*\u001b[49m\u001b[43mkwargs\u001b[49m\u001b[43m)\u001b[49m\n",
      "\u001b[31mFileNotFoundError\u001b[39m: [Errno 2] No such file or directory: '/home/archivo.txt'"
     ]
    }
   ],
   "source": [
    "# Path absoluto\n",
    "\n",
    "path_absoluto = '/home/archivo.txt'\n",
    "\n",
    "with open(path_absoluto, 'rt') as archivo:\n",
    "    lineas = archivo.readlines()\n",
    "    \n",
    "lineas"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "En el código anterior, se intenta abrir un archivo llamado `archivo.txt` una carpeta llamada `home`, ubicada en el directorio raíz (`/`) del sistema de archivos del computador donde se está ejecutando el código. Este código lanzará una excepción, a menos que esta carpeta y este archivo existan."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "'Funciona!\\n'"
      ]
     },
     "execution_count": 3,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "## Path relativo\n",
    "\n",
    "path_relativo = 'data/archivo.txt'\n",
    "\n",
    "with open(path_relativo, 'rt') as archivo:\n",
    "    lineas = archivo.read()\n",
    "    \n",
    "lineas"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "En este código, se intenta abrir un archivo llamado `archivo.txt` que está dentro de una carpeta llamada `data`, que está ubicada en la carpeta donde se está ejecutando este código Python. Este archivo debería leerse sin problemas, ya que el repositorio donde se encuentra este material incluye a esta carpeta y a este archivo."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Un *path* es la dirección de un archivo o de un directorio. Este siempre se divide en dos partes:\n",
    " - El nombre del directorio o `dirname`, que es la carpeta donde se encuentra el archivo o directorio objetivo.\n",
    " - El nombre de archivo o directorio objetivo, *filename* o `basename`, que es el nombre del archivo, incluyendo su extensión, o directorio.\n",
    " \n",
    "En el siguiente código, el módulo `os.path` permite separar un *path* en `dirname` y `basename`."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "path: /carpeta1/carpeta2/imagen.jpg\n",
      "dirname: /carpeta1/carpeta2\n",
      "basename: imagen.jpg\n"
     ]
    }
   ],
   "source": [
    "path1 = '/carpeta1/carpeta2/imagen.jpg'\n",
    "\n",
    "dirname1 = os.path.dirname(path1)\n",
    "basename1 = os.path.basename(path1)\n",
    "\n",
    "print(f'path: {path1}')\n",
    "print(f'dirname: {dirname1}')\n",
    "print(f'basename: {basename1}')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "path: f1/f2/f3\n",
      "dirname: f1/f2\n",
      "basename: f3\n"
     ]
    }
   ],
   "source": [
    "path2 = 'f1/f2/f3'\n",
    "\n",
    "dirname2 = os.path.dirname(path2)\n",
    "basename2 = os.path.basename(path2)\n",
    "\n",
    "print(f'path: {path2}')\n",
    "print(f'dirname: {dirname2}')\n",
    "print(f'basename: {basename2}')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Extensiones de archivo"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Los nombres de archivo suelen terminar con una secuencia, típicamente de tres caracteres, que aparece después de un punto, por ejemplo `.txt`, `.jpg`, `.pdf`, `.mp3` y `.avi`. Esta secuencia de letras se conoce como **extensión** del archivo y sirve para dos objetivos:\n",
    "\n",
    "1. Darle una _pista_ al usuario sobre el tipo de archivo de que se trata, para saber qué hacer con él. Por ejemplo, cómo abrirlo.\n",
    "1. Darle una _pista_ al sistema operativo para saber con qué programa leer el archivo."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "imagen\n",
      ".jpg\n"
     ]
    }
   ],
   "source": [
    "# Podemos usar la función `splitext` para separar la extension del resto del nombre de archivo\n",
    "\n",
    "nombre_sin_extension, extension = os.path.splitext(basename1)\n",
    "print(nombre_sin_extension)\n",
    "print(extension)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Hay que tener en cuenta que, si bien la extensión del archivo sirve para darnos información acerca del tipo de archivo, ésta es **parte del nombre de archivo** y es **sólo una convención**. Una extensión informa del tipo de archivo, pero **no determina el tipo del archivo**.\n",
    "\n",
    "Por ejemplo, a continuación escribiremos un archivo de texto y lo guardaremos con extensión `.jpg`, que indica que el archivo es una imagen en formato JPG. Para un usuario puede verse extraño que un archivo de texto tenga una extensión de imagen, sin embargo, el archivo seguirá siendo un archivo de texto."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [],
   "source": [
    "path = 'data/archivo_de_texto.jpg'\n",
    "\n",
    "with open(path, 'w') as f:\n",
    "    f.writelines(['linea 1\\n', 'linea 2\\n', 'linea 3\\n'])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Si intentas abrir el archivo generado, puede que tu sistema operativo intente erróneamente abrirlo con un visor de imágenes. Sin embargo, si lo abres con tu editor de texto favorito, deberías poder leerlo adecuadamente, ya que es un archivo de texto.\n",
    "\n",
    "Algunos sistemas operativos vienen configurados por defecto para ocultar la extensión de los archivos. Se recomienda fuertemente cambiar esta configuración para poder ver el nombre de archivo completo y evitar confusiones al leer y escribir archivos."
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Problema de portabilidad de *paths*\n",
    "\n",
    "Como se ha mencionado anteriormente, hay diferencias en los caracteres de separación entre sistemas operativos, por ejemplo la ruta `\"Users/Pedro/Libros/python.pdf\"` abriría correctamente un archivo en un sistema Unix (Linux y macOS), pero puede fallar en Windows, ya que la ruta equivalente para ese sistema operativo sería `\"Users\\Pedro\\Libros\\python.pdf\"`.\n",
    "\n",
    "Esto trae un problema de portabilidad de programas, que si explicitan rutas utilizando alguna de estas escrituras y luego se intentan ejecutar en otro sistema operativo, puede que no funcione, ya que la ruta es inválida en ese ambiente.\n",
    "\n",
    "El módulo `os.path` provee muchas funcionalidades para reescribir rutas usando los caracteres de separación nativos del sistema operativo dónde se ejecute el programa. Por ejemplo, el método `os.path.join`:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "'Users/Pedro/Libros/python.pdf'"
      ]
     },
     "execution_count": 8,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "ruta = os.path.join(\"Users\", \"Pedro\", \"Libros\", \"python.pdf\")\n",
    "ruta"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "El método genera automáticamente la ruta equivalente al concatenar los argumentos que se le den, utilizando el separador del sistema operativo dónde se ejecute. Se recomienda **fuertemente** trabajar con *paths* de esta forma, y así asegurar portabilidad de sus programas."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Navegación entre directorios\n",
    "\n",
    "La librería `os` también nos provee funciones para ver el contenido de directorios y recorrer sus distintos archivos y directorios."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "La más conocida, es la función `listdir` que al entregarle una ruta de un directorio, entrega una lista con los nombres de directorios y archivos que se encuentran directamente dentro de ese directorio. Por ejemplo, al ejecutarlo con la ruta `\"data\"`, vemos el contenido de la carpeta `data` dentro de este repositorio:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "['screenshot_1.png',\n",
       " 'example.png',\n",
       " '1.png',\n",
       " 'archivo_de_texto.jpg',\n",
       " 'archivo_pesado.png',\n",
       " 'archivo.txt',\n",
       " 'gato',\n",
       " 'screenshot_6.png',\n",
       " 'screenshot_5.png',\n",
       " 'files.png',\n",
       " 'screenshot_4.png',\n",
       " 'merge_conflict.png',\n",
       " '2.png',\n",
       " 'screenshot_3.png',\n",
       " 'problema_sincronizacion.png',\n",
       " 'screenshot_2.png']"
      ]
     },
     "execution_count": 9,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "lista_de_contenidos = os.listdir(\"data\")\n",
    "lista_de_contenidos"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Vemos los nombres de tres archivos, y el de una carpeta `gato` que se utiliza en un ejemplo siguiente. Notar que no aparecen en esta lista los contenidos de `gato`, ya que `listdir` solo lista los contenidos directamente almacenados."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "['juego_2.txt', 'juego_1.txt']"
      ]
     },
     "execution_count": 10,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "lista_de_contenidos = os.listdir(os.path.join(\"data\", \"gato\"))\n",
    "lista_de_contenidos"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "En cambio, la función `walk` nos permite poder obtener las rutas de un directorio, de sus subdirectorios y de sus archivos. Esto nos permite poder navegar dentro de una carpeta y ver, recursivamente, todo lo que contiene.\n",
    "\n",
    "El siguiente código muestra como se utiliza:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Raíz: data\n",
      "\n",
      "Archivos:\n",
      "data/screenshot_1.png\n",
      "data/example.png\n",
      "data/1.png\n",
      "data/archivo_de_texto.jpg\n",
      "data/archivo_pesado.png\n",
      "data/archivo.txt\n",
      "data/screenshot_6.png\n",
      "data/screenshot_5.png\n",
      "data/files.png\n",
      "data/screenshot_4.png\n",
      "data/merge_conflict.png\n",
      "data/2.png\n",
      "data/screenshot_3.png\n",
      "data/problema_sincronizacion.png\n",
      "data/screenshot_2.png\n",
      "\n",
      "Directorios:\n",
      "data/gato\n",
      "------------------------------\n",
      "Raíz: data/gato\n",
      "\n",
      "Archivos:\n",
      "data/gato/juego_2.txt\n",
      "data/gato/juego_1.txt\n",
      "\n",
      "Directorios:\n",
      "------------------------------\n"
     ]
    }
   ],
   "source": [
    "for raiz, directorios, archivos in os.walk(\"data\", topdown=True):\n",
    "    print(\"Raíz:\", raiz)\n",
    "    print()\n",
    "    print(\"Archivos:\")\n",
    "    for archivo in archivos:\n",
    "        print(os.path.join(raiz, archivo))\n",
    "    print()\n",
    "    print(\"Directorios:\")\n",
    "    for directorio in directorios:\n",
    "        print(os.path.join(raiz, directorio))\n",
    "    print(\"-\" * 30)"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "El parámetro `topdown` nos permite decidir si la navegación sobre una carpeta será desde el directorio raíz (`topdown=True`) o desde sus elementos hojas (`topdown=False`)."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Un ejemplo de lectura y escritura básico\n",
    "\n",
    "Finalmente, se muestra un ejemplo de lectura y escritura de archivos de texto simple. El siguiente código busca leer y guardar tableros del juego **Gato**. \n",
    "\n",
    "Comenzaremos leyendo un tablero existente. Dentro de la carpeta `gato` (dentro de la carpeta `data`) existe un archivo de texto simple con un tablero de Gato: `juego_1.txt`. Para abrirlo, usamos la función `open` de Python, que recibe la ruta de un archivo. Utilizaremos la función `os.path.join` para definir la ruta de forma portable:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "['X,-,O\\n', '-,X,-\\n', '-,-,-\\n']"
      ]
     },
     "execution_count": 12,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "ruta_juego_1 = os.path.join(\"data\", \"gato\", \"juego_1.txt\")\n",
    "archivo = open(ruta_juego_1, \"rt\")\n",
    "archivo.readlines()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "El argumento `\"rt\"` especifica el modo de apertura del archivo. La `\"r\"` significa que es en modo de lectura (*read*), y la `\"t\"` en forma de texto. Más adelante en el curso estudiaremos otra forma de lectura y escritura de archivos.\n",
    "\n",
    "Mediante el método `readlines` obtenemos una lista de las líneas del archivo de texto. Vemos que el tablero se representa mediante filas en cada línea, y cada una se separa por comas para cada posición del tablero. Hay una `\"X\"` o una `\"O\"` para representar jugadas de los jugadores, o `\"-\"` para un espacio vacío.\n",
    "\n",
    "Antes de procesar este contenido, debemos cerrar el archivo que acabamos de abrir:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "metadata": {},
   "outputs": [],
   "source": [
    "archivo.close()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Cada vez que se use la función `open` de esta forma, el archivo usado debe ser cerrado utilizando `close`. Una alternativa al uso de `close`, es abrir un archivo utilizando un ambiente generado con la sentencia `with`, como a continuación:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "metadata": {},
   "outputs": [],
   "source": [
    "with open(ruta_juego_1, \"rt\") as archivo:\n",
    "    lineas = archivo.readlines()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "La ventaja de esta forma, es que al salir de la indentación generada por la sentencia `with` el archivo se cierra automáticamente y no es necesario utilizar `close`. Si vemos la variable generada, notamos que el contenido del archivo se obtuvo de la misma forma y se almacenó:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "['X,-,O\\n', '-,X,-\\n', '-,-,-\\n']"
      ]
     },
     "execution_count": 15,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "lineas"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Notamos que esta forma de almacenar el tablero sigue siendo poco conveniente, ya que usa *strings* para cada fila. El siguiente código limpia y separa cada fila en una lista con las posiciones separadas:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "['X', '-', 'O']\n",
      "['-', 'X', '-']\n",
      "['-', '-', '-']\n"
     ]
    }
   ],
   "source": [
    "tablero = []\n",
    "for linea in lineas:\n",
    "    fila = linea.strip().split(',')\n",
    "    print(fila)\n",
    "    tablero.append(fila)"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "El método `strip` por defecto remueve espacios o saltos de líneas al comienzo o al final de un *string*, en este caso remueve `\"\\n\"` de cada línea. Luego `split` separa el string según un carácter, en este caso, utilizamos `\",\"` para separar.\n",
    "\n",
    "Así, generamos una lista de dos dimensiones, con las posiciones del tablero ordenadas:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "[['X', '-', 'O'], ['-', 'X', '-'], ['-', '-', '-']]"
      ]
     },
     "execution_count": 17,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "tablero"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "💡 **ProTip:** Además de usar `.strip()` para eliminar los saltos de línea al final de cada línea, también es posible evitar este paso si se abre el archivo con el parámetro `newline=''`.\n",
    "\n",
    "```python\n",
    "with open(ruta_juego_1, \"rt\", newline='') as archivo:\n",
    "    lineas = archivo.readlines()\n",
    "\n",
    "for linea in lineas:\n",
    "    fila = linea.split(',')  # ya no es necesario usar .strip()\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Ahora aplicaremos una nueva jugada por el jugador `\"O\"`, modificando una de las posiciones del tablero:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 18,
   "metadata": {},
   "outputs": [],
   "source": [
    "tablero[2][2] = 'O'"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 19,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "['X', '-', 'O']\n",
      "['-', 'X', '-']\n",
      "['-', '-', 'O']\n"
     ]
    }
   ],
   "source": [
    "for fila in tablero:\n",
    "    print(fila)"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Y finalmente guardaremos el resultado en otro archivo. Nuevamente, definiremos la ruta utilizando `os.path.join`, y para escribir en archivos también podemos usar la sentencia `with`:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 20,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "X,-,O\n",
      "\n",
      "-,X,-\n",
      "\n",
      "-,-,O\n",
      "\n"
     ]
    }
   ],
   "source": [
    "ruta_juego_2 = os.path.join(\"data\", \"gato\", \"juego_2.txt\")\n",
    "\n",
    "with open(ruta_juego_2, \"wt\") as archivo:\n",
    "    for fila in tablero:\n",
    "        fila_en_texto = \",\".join(fila) + \"\\n\"\n",
    "        print(fila_en_texto)\n",
    "        archivo.write(fila_en_texto)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Aquí, el método `join` de un *string* recibe una lista de *strings* y genera otro *string* más largo que es el resultado de concatenar los *strings* pero alternando el contenido usando el *string* original, en este caso, `\",\"`.\n",
    "\n",
    "El resultado del tablero quedó almacenado en `\"data/gato/juego_2.txt\"`."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Pathlib\n",
    "\n",
    "Este es un módulo de Python que ofrece clases capaces de representar las rutas del sistema de archivos. Es decir, utiliza una interfaz orientada a objetos para manipular rutas de archivos y directorios de manera eficiente. Podrás encontrar la documentación completa en español en [el siguiente link](https://docs.python.org/es/3/library/pathlib.html#pathlib.Path)."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 21,
   "metadata": {},
   "outputs": [],
   "source": [
    "from pathlib import Path"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "En el código anterior, estamos importando la clase Path con todos sus atributos y métodos."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 22,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Creamos la ruta instanciando un objeto de la clase Path\n",
    "path = Path(\"data/archivo.txt\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Al ser un objeto, cuenta con diversos atributos y métodos. A continuación detallaremos los más relevantes."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 23,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Nombre del archivo: archivo.txt\n",
      "Extensión del archivo: .txt\n",
      "Es un directorio: False\n",
      "Es un archivo: True\n",
      "Ruta absoluta: /home/danic/Avanzada/Contenidos/semana-02-entorno_de_trabajo/data/archivo.txt\n",
      "El archivo existe en la ruta indicada\n",
      "\n",
      "Leer el contenido de un archivo de texto:\n",
      "Funciona!\n",
      "\n",
      "\n",
      "Recorriendo el directorio de la carpeta data:\n",
      "data/screenshot_1.png\n",
      "data/example.png\n",
      "data/1.png\n",
      "data/archivo_de_texto.jpg\n",
      "data/archivo_pesado.png\n",
      "data/archivo.txt\n",
      "data/gato\n",
      "data/screenshot_6.png\n",
      "data/screenshot_5.png\n",
      "data/files.png\n",
      "data/screenshot_4.png\n",
      "data/merge_conflict.png\n",
      "data/2.png\n",
      "data/screenshot_3.png\n",
      "data/problema_sincronizacion.png\n",
      "data/screenshot_2.png\n"
     ]
    }
   ],
   "source": [
    "print(\"Nombre del archivo:\", path.name)\n",
    "\n",
    "print(\"Extensión del archivo:\", path.suffix)\n",
    "\n",
    "print('Es un directorio:', path.is_dir())\n",
    "\n",
    "print('Es un archivo:',path.is_file())\n",
    "\n",
    "path_absoluto = path.absolute()\n",
    "print('Ruta absoluta:', path_absoluto)\n",
    "\n",
    "if path.exists():\n",
    "    print(\"El archivo existe en la ruta indicada\")\n",
    "\n",
    "print('\\nLeer el contenido de un archivo de texto:')\n",
    "contenido = path.read_text()\n",
    "print(contenido)\n",
    "\n",
    "print('\\nRecorriendo el directorio de la carpeta data:')\n",
    "directorio = Path(\"./data/\")\n",
    "for archivo in directorio.iterdir():\n",
    "    print(archivo)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "El objeto instanciado de la clase Path también permite escritura y lectura, usado en conjunto con la función `os` de Python."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 24,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Funciona!\n",
      "\n"
     ]
    }
   ],
   "source": [
    "with path.open(mode='r') as archivo:\n",
    "    contenido = archivo.read()\n",
    "    print(contenido)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 25,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Podría funcionar mejor?\n",
      "Recordar que el modo 'w' sobre escribe lo existente!\n"
     ]
    }
   ],
   "source": [
    "path1 = Path(\"./data/nuevo_archivo.txt\")\n",
    "with path1.open(mode='w') as archivo:\n",
    "    archivo.write(\"Podría funcionar mejor?\\n\")\n",
    "    archivo.write(\"Recordar que el modo 'w' sobre escribe lo existente!\")\n",
    "\n",
    "with path1.open(mode='r') as archivo:\n",
    "    contenido = archivo.read()\n",
    "    print(contenido)\n"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "3.12.9",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.12.9"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 4
}
