Herramientas de usuario

Herramientas del sitio


informatica:programacion:cursos:programacion_desde_cero_con_python:archivos_y_persistencia

¡Esta es una revisión vieja del documento!


Archivos y persistencia

Guardar y recuperar datos entre ejecuciones.

Este módulo forma parte del curso de programación desde cero con Python

Módulo 13. Archivos y persistencia

Hasta ahora, todos tus programas han vivido en la memoria RAM: al cerrar el programa, los datos desaparecen. Para que la información sobreviva entre ejecuciones, necesitas persistencia, y la forma más directa de lograrla es guardar los datos en archivos. Este módulo te enseña a leer y escribir archivos de texto, a gestionar rutas de forma portable entre Windows y Linux con pathlib, a trabajar con formatos estructurados como CSV y JSON, y a usar gestores de contexto (with) para no dejar recursos abiertos. También conocerás las novedades de Python 3.15 que afectan directamente a la persistencia: UTF-8 como codificación predeterminada y mejoras en módulos como shelve.


1. Título y alcance

Título: Archivos y persistencia: guardar y recuperar datos entre ejecuciones.

Alcance: Comprender qué es un archivo y por qué es necesario para la persistencia; abrir archivos en modo lectura, escritura y añadido; usar gestores de contexto (with); gestionar rutas de forma portable con pathlib; leer y escribir texto con codificación UTF-8 (predeterminada en Python 3.15); trabajar con CSV y JSON; conocer alternativas de persistencia como shelve (con su nuevo método reorganize()) y pickle; y manejar errores de archivos y permisos con try/except. Se presupone el manejo de excepciones (Módulo 11), funciones (Módulo 9) y colecciones (Módulo 8). No se profundiza en bases de datos relacionales (se introducirá sqlite3 en el Módulo 20).


2. Objetivos de aprendizaje

Al terminar este módulo serás capaz de:

1. Explicar qué es un archivo, qué es una ruta y por qué la persistencia es esencial. 2. Abrir archivos con open() en los modos "r", "w", "a" y "x", y entender qué hace cada uno. 3. Leer un archivo completo, línea a línea, o en fragmentos. 4. Escribir y añadir texto a un archivo, garantizando que se cierre correctamente con with. 5. Construir rutas portables con pathlib.Path, incluyendo rutas absolutas, relativas y la resolución de ~ (directorio personal). 6. Leer y escribir CSV con el módulo csv, y JSON con el módulo json. 7. Comprender la codificación de caracteres y por qué UTF-8 es el valor predeterminado en Python 3.15. 8. Manejar FileNotFoundError, PermissionError y UnicodeDecodeError de forma específica. 9. Conocer shelve como almacén clave-valor persistente y su nuevo método reorganize() en Python 3.15. 10. Aplicar buenas prácticas: usar with, no cargar archivos gigantes en memoria, validar rutas y no confiar en la codificación del sistema.


3. Conocimientos previos

- Del Módulo 8: Listas, diccionarios y comprensiones. - Del Módulo 9: Funciones, return y docstrings. - Del Módulo 11: Excepciones, try/except y logging. - Del Módulo 12: Módulos e importaciones (usarás import csv, import json, from pathlib import Path). - Ningún conocimiento de archivos, rutas ni persistencia.


4. Introducción motivadora: el programa que olvida todo al cerrarse

Imagina una agenda de contactos que, al cerrar la aplicación, pierde todos los nombres y teléfonos introducidos. No es una agenda: es un formulario temporal. Lo mismo ocurre con cualquier programa que gestiona datos: sin persistencia, cada ejecución empieza de cero. Los archivos son el mecanismo más sencillo y universal para que la información sobreviva. Y no solo para programas de escritorio: los archivos de configuración, los registros (logs), los datos exportados y los archivos CSV que comparten otras herramientas son parte del día a día de cualquier desarrollador.

Python ofrece herramientas de alto nivel para trabajar con archivos sin pelearse con los detalles del sistema operativo. pathlib unifica las rutas de Windows y Linux; with garantiza que los archivos se cierren incluso si hay errores; y los módulos csv y json evitan que tengas que parsear formatos a mano. En este módulo aprenderás a usar todo ello con seguridad y buenas prácticas.


5. Explicación paso a paso

5.1. ¿Qué es un archivo y qué es una ruta?

Un archivo es una secuencia de bytes almacenada en un dispositivo (disco, SSD, etc.) que el sistema operativo identifica mediante un nombre y una ruta. La ruta puede ser:

- Absoluta: describe la ubicación completa desde la raíz del sistema.

  1. Windows: C:\Users\Ana\documentos\datos.txt
  2. Linux: /home/ana/documentos/datos.txt

- Relativa: describe la ubicación respecto al directorio de trabajo actual.

  1. datos.txt (en la carpeta actual)
  2. ../datos.txt (en la carpeta padre)
[WINDOWS / LINUX] Windows usa la barra invertida \ y distingue unidades (C:, D:). Linux usa la barra normal / y tiene una única raíz /. Escribir rutas manualmente con el separador del sistema es una fuente constante de errores de portabilidad. pathlib resuelve este problema.

5.2. `pathlib`: rutas portables

pathlib es el módulo de la biblioteca estándar para manipular rutas como objetos. La clase principal es Path.

from pathlib import Path
 
# Ruta relativa
ruta = Path("datos") / "contactos.txt"
 
# Ruta absoluta (depende del sistema)
ruta_abs = Path.cwd() / "datos" / "contactos.txt"
 
# Directorio personal del usuario
ruta_home = Path.home() / "documentos" / "notas.txt"
 
print(ruta)          # datos/contactos.txt (en ambos sistemas)
print(ruta_abs)      # /home/ana/proyecto/datos/contactos.txt o C:\...\datos\contactos.txt
print(ruta_home)     # /home/ana/documentos/notas.txt o C:\Users\Ana\documentos\notas.txt

El operador / une segmentos de ruta de forma portable: en Windows produce \ y en Linux produce / automáticamente. No necesitas preocuparte por el separador.

Operaciones útiles con Path:

OperaciónEjemploResultado
Comprobar existenciaruta.exists()True o False
Comprobar si es archivoruta.is_file()True o False
Comprobar si es directorioruta.is_dir()True o False
Obtener nombreruta.name"contactos.txt"
Obtener extensiónruta.suffix".txt"
Obtener padreruta.parentPath("datos")
Crear directorioruta.mkdir(parents=True, exist_ok=True)Crea la carpeta si no existe
Listar contenidolist(ruta.parent.iterdir())Lista de rutas hijas
[PYTHON] Path es un objeto, no una cadena. Puedes pasarlo a open() directamente; Python lo convierte a la ruta del sistema automáticamente. Esto evita concatenaciones manuales y errores de separador. En Python 3.15, se elimina PurePath.is_reserved() (deprecado desde 3.13); si necesitas comprobar si una ruta está reservada en Windows, usa os.path.isreserved().
[ERROR HABITUAL] Usar Path("datos") / "contactos.txt" y luego intentar concatenar con +: ruta + ".txt" falla porque Path no admite + con cadenas. Usa ruta.with_suffix(".txt") o construye la ruta desde el principio.

5.3. `open()` y los modos de apertura

La función incorporada open() abre un archivo y devuelve un objeto de archivo (file object). Su firma básica es:

open(ruta, mode="r", encoding=None)

Los modos principales son:

ModoSignificado¿Crea el archivo?¿Trunca el contenido?
"r"Lectura (predeterminado).No (falla si no existe).No
"w"Escritura.Sí.Sí (vacía el archivo antes de escribir).
"a"Añadido (append).Sí.No (escribe al final).
"x"Creación exclusiva.Sí.Falla si el archivo ya existe.
"b"Modo binario (se combina: "rb", "wb").——
"+"Lectura y escritura (se combina: "r+", "w+").——
[ERROR HABITUAL] Abrir un archivo en modo "w" cuando solo querías leerlo. "w" vacía el archivo inmediatamente, antes de que escribas nada. Si el archivo contenía datos, los pierdes. Para leer, usa "r"; para añadir sin borrar, usa "a".
[BUENA PRÁCTICA] Especifica siempre encoding="utf-8" al abrir archivos de texto, incluso aunque Python 3.15 use UTF-8 por defecto. Ser explícito documenta la intención y protege el código si se ejecuta en un entorno con una configuración regional diferente. La guía de Python 3.15 recomienda auditar el código que abre archivos sin encoding para evitar sorpresas al migrar.

5.4. Gestores de contexto: `with`

El objeto de archivo debe cerrarse después de usarlo. Si no se cierra, los datos pueden no escribirse completamente en el disco, o el archivo puede quedar bloqueado. La forma segura e idiomática de garantizar el cierre es usar with:

with open("datos.txt", "w", encoding="utf-8") as archivo:
    archivo.write("Hola, mundo\n")
# Al salir del bloque, el archivo se cierra automáticamente,
# incluso si se produce una excepción.

Sin with, tendrías que llamar a archivo.close() manualmente y asegurarte de que se ejecute incluso en caso de error (con try/finally). with elimina esa carga.

[PYTHON] with es un gestor de contexto (context manager). Cualquier objeto que implemente los métodos especiales __enter__ y __exit__ puede usarse con with. Los archivos son el ejemplo más común, pero también se usan para conexiones de red, bloqueos, transacciones de bases de datos, etc. En el Módulo 15 profundizarás en cómo crear tus propios gestores de contexto.
[ERROR HABITUAL] Usar el archivo fuera del bloque with:
with open("datos.txt") as f:
    contenido = f.read()
print(f.read())  # ValueError: I/O operation on closed file

El archivo está cerrado fuera del bloque. Guarda lo que necesites dentro del with.

5.5. Lectura de archivos

Leer todo el contenido de una vez:

with open("datos.txt", "r", encoding="utf-8") as f:
    contenido = f.read()
print(contenido)

Leer línea a línea (recomendado para archivos grandes):

with open("datos.txt", "r", encoding="utf-8") as f:
    for linea in f:
        print(linea.rstrip("\n"))  # Elimina el salto de línea

Iterar sobre el archivo es perezoso: no carga todo el contenido en memoria, lo que permite procesar archivos de gigabytes sin agotar la RAM.

Leer todas las líneas en una lista:

with open("datos.txt", "r", encoding="utf-8") as f:
    lineas = f.readlines()
[BUENA PRÁCTICA] Para archivos grandes, itera línea a línea en lugar de usar .read() o .readlines(). .read() carga todo en memoria; .readlines() crea una lista con todas las líneas. La iteración directa (for linea in f) es la opción más eficiente en memoria.
[ERROR HABITUAL] Olvidar que cada línea leída incluye el carácter de salto de línea \n al final. Usa .rstrip("\n") o .strip() para eliminarlo antes de procesar el texto.

5.6. Escritura de archivos

Escribir (sobrescribe el archivo):

with open("salida.txt", "w", encoding="utf-8") as f:
    f.write("Línea 1\n")
    f.write("Línea 2\n")

Añadir (conserva el contenido existente):

with open("salida.txt", "a", encoding="utf-8") as f:
    f.write("Línea 3\n")
[PYTHON] .write() no añade un salto de línea automáticamente. Debes incluirlo explícitamente (\n) si quieres que cada escritura ocupe una línea distinta. print() sí añade un salto, pero no escribe en archivos; escribe en la salida estándar. Para escribir con formato, puedes usar f.write(f"{valor}\n").

5.7. Codificación de caracteres y UTF-8

La codificación es la regla que traduce caracteres (como ñ, á, 中) a bytes. Si lees un archivo con una codificación distinta a la que se usó para escribirlo, obtendrás caracteres corruptos (mojibake) o un UnicodeDecodeError.

[PYTHON] Python 3.15 hace que UTF-8 sea la codificación predeterminada para la entrada/salida de texto en todas las plataformas (PEP 686). Antes, Windows usaba a menudo cp1252, lo que causaba que un archivo con tildes escrito en Linux se viera mal en Windows. Ahora, open("datos.txt") sin encoding usa UTF-8 en ambos sistemas, lo que elimina la principal fuente de problemas de portabilidad.

Aun así, sé explícito: encoding="utf-8" documenta la intención y protege el código si se ejecuta en un entorno donde el modo UTF-8 está desactivado (PYTHONUTF8=0).

[ERROR HABITUAL] UnicodeDecodeError al leer un archivo que no está en UTF-8 (por ejemplo, un CSV exportado por una herramienta antigua en latin-1). Solución: especifica la codificación correcta (encoding="latin-1") o convierte el archivo a UTF-8 antes de procesarlo. En CSV generados en Windows con BOM, puede ser necesario usar encoding="utf-8-sig".

5.8. CSV: datos tabulares

El módulo csv de la biblioteca estándar lee y escribe archivos de valores separados por comas. Es el formato habitual para exportar e importar datos de hojas de cálculo y bases de datos.

Leer un CSV:

import csv
 
with open("datos.csv", newline="", encoding="utf-8") as f:
    lector = csv.reader(f)
    for fila in lector:
        print(fila)  # Cada fila es una lista de cadenas

Leer como diccionarios (con cabecera):

with open("datos.csv", newline="", encoding="utf-8") as f:
    lector = csv.DictReader(f)
    for fila in lector:
        print(fila["nombre"], fila["edad"])

Escribir un CSV:

with open("salida.csv", "w", newline="", encoding="utf-8") as f:
    escritor = csv.writer(f)
    escritor.writerow(["nombre", "edad"])
    escritor.writerow(["Ana", 25])
    escritor.writerow(["Luis", 30])
[PYTHON] El argumento newline="" es importante al trabajar con CSV: evita que el módulo csv añada saltos de línea extra en Windows. En Python 3.15, csv.Sniffer (la herramienta para detectar el delimitador) es hasta un 1,6 veces más rápida, lo que mejora el rendimiento al procesar archivos grandes. Además, las reglas de quoting QUOTE_NOTNULL y QUOTE_STRINGS (introducidas en versiones anteriores) ya se aplican de forma consistente en el lector y el escritor.
[ERROR HABITUAL] Abrir un CSV sin newline="" en Windows puede producir líneas en blanco entre filas al escribir. Siempre incluye newline="" al abrir archivos CSV para escritura.

5.9. JSON: datos estructurados

JSON (JavaScript Object Notation) es un formato de texto ligero para intercambiar datos estructurados. Es el estándar de facto en APIs y archivos de configuración.

Leer JSON:

import json
 
with open("datos.json", "r", encoding="utf-8") as f:
    datos = json.load(f)
 
print(datos["nombre"])
print(datos["intereses"])

Escribir JSON:

import json
 
datos = {
    "nombre": "Ana",
    "edad": 25,
    "intereses": ["Python", "ajedrez"]
}
 
with open("datos.json", "w", encoding="utf-8") as f:
    json.dump(datos, f, ensure_ascii=False, indent=2)

- ensure_ascii=False permite que los caracteres no ASCII (tildes, ñ, emojis) se escriban tal cual, en lugar de como secuencias \uXXXX. - indent=2 formatea el JSON con sangría para que sea legible por humanos.

[PYTHON] json convierte entre objetos Python y texto JSON: diccionarios ↔ objetos, listas ↔ arrays, cadenas ↔ strings, números ↔ numbers, True/False/None ↔ true/false/null. No todos los tipos de Python son serializables (por ejemplo, un set no lo es); en esos casos, json.dump lanza TypeError.
[AMPLIACIÓN] En Python 3.15, puedes usar lazy import json para retrasar la carga del módulo hasta que se use por primera vez, lo que acelera el arranque de aplicaciones que solo necesitan JSON en ciertos caminos. También puedes declarar __lazy_modules__ = ["json"] para obtener el mismo efecto sin la palabra clave, manteniendo compatibilidad con versiones anteriores.

5.10. Otras formas de persistencia: `shelve` y `pickle`

shelve: un almacén clave-valor persistente que se comporta como un diccionario, pero cuyos datos sobreviven al cierre del programa. Ideal para pequeños proyectos que necesitan guardar objetos Python sin montar una base de datos.

import shelve
 
with shelve.open("agenda.db") as db:
    db["ana"] = {"telefono": "123", "email": "ana@example.com"}
    print(db["ana"])
[PYTHON] En Python 3.15, shelve (y los módulos dbm.dumb y dbm.sqlite3) incorporan un nuevo método reorganize() que compacta el archivo en disco eliminando el espacio liberado por entradas borradas. Antes, los archivos shelve podían crecer indefinidamente aunque se eliminaran datos; ahora puedes llamar a db.reorganize() para recuperar ese espacio. Ten en cuenta que puede aumentar temporalmente el uso de disco y que no todos los backends de dbm lo soportan.

pickle: serializa objetos Python arbitrarios a bytes y viceversa. Es potente, pero inseguro si los datos provienen de una fuente no confiable, porque puede ejecutar código arbitrario al deserializar. Úsalo solo con datos propios o de confianza.

[BUENA PRÁCTICA] Para archivos de configuración y datos compartidos, prefiere JSON (legible, portable, seguro). Para datos internos de la aplicación que no necesitan ser legibles por humanos, pickle o shelve pueden ser adecuados, pero documenta claramente que el archivo no debe abrirse con datos externos.

5.11. Errores de archivos y permisos

Las operaciones con archivos son propensas a errores que debes manejar:

- FileNotFoundError: el archivo no existe (al leer) o la ruta es incorrecta. - PermissionError: no tienes permisos para leer o escribir en esa ubicación. - IsADirectoryError: intentas abrir un directorio como si fuera un archivo. - UnicodeDecodeError: la codificación no coincide con el contenido.

from pathlib import Path
import logging
 
def leer_configuracion(ruta):
    """Lee un archivo de configuración y devuelve su contenido, o None si falla."""
    ruta = Path(ruta)
    try:
        with open(ruta, "r", encoding="utf-8") as f:
            return f.read()
    except FileNotFoundError:
        logging.error("No se encontró el archivo: %s", ruta)
        return None
    except PermissionError:
        logging.error("Sin permisos para leer: %s", ruta)
        return None
    except UnicodeDecodeError:
        logging.error("Codificación no válida en: %s", ruta)
        return None
[ERROR HABITUAL] Capturar Exception de forma genérica al abrir archivos. Es mejor capturar los tipos específicos (FileNotFoundError, PermissionError, etc.) para no ocultar errores de programación. Sigue las recomendaciones del Módulo 11.

6. Ejemplos comentados

Ejemplo 1: Leer un archivo de texto y contar líneas.

from pathlib import Path
 
def contar_lineas(ruta):
    """Devuelve el número de líneas de un archivo de texto."""
    ruta = Path(ruta)
    if not ruta.is_file():
        raise FileNotFoundError(f"No existe el archivo: {ruta}")
    with open(ruta, "r", encoding="utf-8") as f:
        return sum(1 for _ in f)
 
print(contar_lineas("datos.txt"))

Seguimiento: sum(1 for _ in f) es una expresión generadora que cuenta las líneas sin cargar el archivo en memoria. Path.is_file() comprueba la existencia antes de abrir. La excepción se propaga si el archivo no existe.

Ejemplo 2: Escribir y leer un CSV de contactos.

import csv
from pathlib import Path
 
ruta = Path("contactos.csv")
 
# Escribir
with open(ruta, "w", newline="", encoding="utf-8") as f:
    escritor = csv.writer(f)
    escritor.writerow(["nombre", "telefono"])
    escritor.writerow(["Ana", "123456789"])
    escritor.writerow(["Luis", "987654321"])
 
# Leer como diccionarios
with open(ruta, newline="", encoding="utf-8") as f:
    lector = csv.DictReader(f)
    for contacto in lector:
        print(f"{contacto['nombre']}: {contacto['telefono']}")

Seguimiento: csv.DictReader usa la primera fila como cabecera y devuelve cada fila como un diccionario. newline="" evita problemas de saltos de línea en Windows.

Ejemplo 3: Guardar y recuperar configuración en JSON.

import json
from pathlib import Path
 
RUTA_CONFIG = Path("config.json")
 
def guardar_config(config):
    """Guarda un diccionario de configuración en JSON."""
    with open(RUTA_CONFIG, "w", encoding="utf-8") as f:
        json.dump(config, f, ensure_ascii=False, indent=2)
 
def cargar_config():
    """Carga la configuración desde JSON, o devuelve un diccionario vacío."""
    try:
        with open(RUTA_CONFIG, "r", encoding="utf-8") as f:
            return json.load(f)
    except FileNotFoundError:
        return {}
 
guardar_config({"tema": "oscuro", "idioma": "es"})
print(cargar_config())

Seguimiento: json.dump escribe el diccionario; json.load lo lee. ensure_ascii=False preserva los caracteres acentuados. FileNotFoundError se maneja devolviendo un diccionario vacío, un patrón común para configuraciones opcionales.


7. Seguimiento de la ejecución

Cuando abres un archivo con open(), Python solicita al sistema operativo un descriptor de archivo (un identificador numérico) y crea un objeto de archivo en memoria. Al leer, el sistema operativo transfiere bytes del disco al buffer del programa. Al escribir, los datos van al buffer y se escriben físicamente en el disco cuando el buffer se llena o cuando el archivo se cierra.

El bloque with garantiza que el archivo se cierre al salir, lo que fuerza el vaciado del buffer (flush) y libera el descriptor. Si el programa termina abruptamente sin cerrar el archivo, parte de los datos podría perderse.

Al iterar sobre un archivo (for linea in f), Python lee el archivo en bloques internos y devuelve una línea cada vez, sin cargar todo el contenido en memoria. Esto es lo que permite procesar archivos enormes con un uso de memoria constante.


8. Errores habituales

ErrorCausaCómo diagnosticarlo
FileNotFoundErrorLa ruta no existe o es incorrecta.Comprueba con Path(ruta).exists(). Usa Path.cwd() para ver el directorio actual.
PermissionErrorSin permisos de lectura/escritura.Verifica los permisos del archivo o elige otra ubicación (por ejemplo, Path.home()).
UnicodeDecodeErrorCodificación incorrecta al leer.Especifica encoding="utf-8" o la codificación correcta. En CSV de Windows, prueba utf-8-sig.
Archivo vacío tras escribirSe abrió en modo "w" y no se escribió nada, o no se cerró el archivo.Usa with para garantizar el cierre. Revisa que .write() se haya llamado.
Líneas en blanco en CSV en WindowsFalta newline="" al abrir.Añade newline="" en open().
TypeError al serializar JSONEl objeto contiene un tipo no serializable (por ejemplo, set).Convierte el objeto a un tipo serializable (lista, diccionario) antes de json.dump.
ValueError: I/O operation on closed fileSe usa el archivo fuera del bloque with.Guarda el contenido en una variable dentro del with y úsalo después.
[ERROR HABITUAL] El error de “archivo vacío” suele deberse a no cerrar el archivo. Sin with ni .close(), el buffer puede no haberse vaciado en el disco. with es la solución.

9. Particularidades de Python frente a otros lenguajes

- pathlib como abstracción de rutas: en C, las rutas son cadenas y el programador debe gestionar los separadores. En Java, java.nio.file.Path ofrece una abstracción similar. pathlib es la forma idiomática en Python y elimina las diferencias entre Windows y Linux. - with como gestor de contexto: Python popularizó este patrón para garantizar la limpieza de recursos. En C, se usan goto o limpieza manual; en Java, try-with-resources. with es más legible y difícil de olvidar. - UTF-8 como predeterminado (Python 3.15): otros lenguajes dependen de la configuración regional del sistema. Python 3.15 unifica el comportamiento entre plataformas, lo que simplifica la portabilidad. - csv y json en la biblioteca estándar: en C, tendrías que parsear a mano o usar bibliotecas externas. Python incluye ambos módulos, lo que reduce las dependencias. - shelve como diccionario persistente: es una solución ligera y pythonica para pequeños proyectos, sin SQL ni esquemas.


10. Buenas prácticas

Incorrecto (sin with, sin encoding, ruta manual):

f = open("C:\\datos\\archivo.txt", "w")
f.write("Hola")
# Si hay una excepción antes de f.close(), el archivo queda abierto y los datos pueden perderse.

Mejor (portable, seguro y explícito):

from pathlib import Path
 
ruta = Path.home() / "datos" / "archivo.txt"
ruta.parent.mkdir(parents=True, exist_ok=True)
with open(ruta, "w", encoding="utf-8") as f:
    f.write("Hola\n")
[BUENA PRÁCTICA] Usa pathlib para construir rutas, with para abrir archivos, encoding="utf-8" explícito, y maneja las excepciones específicas. Itera sobre el archivo en lugar de cargarlo completo si es grande. Y no uses pickle con datos de fuentes no confiables.

11. Windows y Linux

- Rutas: pathlib unifica el separador. Path("datos") / "archivo.txt" produce datos\archivo.txt en Windows y datos/archivo.txt en Linux. - Codificación: con Python 3.15, UTF-8 es el predeterminado en ambos sistemas. Aun así, especifica encoding="utf-8" para mayor claridad y compatibilidad. - CSV y newline="": es especialmente importante en Windows, donde el módulo csv puede añadir \r\n adicionales si no se especifica. - Permisos: en Linux, los permisos de archivo (lectura, escritura, ejecución) son más estrictos. Si obtienes PermissionError, revisa los permisos con ls -l y asegúrate de que el usuario tiene acceso. - Rutas reservadas en Windows: PurePath.is_reserved() se elimina en Python 3.15. Usa os.path.isreserved() si necesitas comprobar nombres como CON, PRN, AUX, etc.

[WINDOWS / LINUX] Si un script funciona en Linux pero falla en Windows con FileNotFoundError, revisa las mayúsculas y minúsculas: Windows no distingue, Linux sí. Path("Datos.txt") no encontrará datos.txt en Linux.

12. Ejercicios guiados

Ejercicio guiado 1: Leer y mostrar un archivo.

1. Crea un archivo notas.txt con tres líneas de texto. 2. Escribe un programa que lo lea línea a línea y muestre cada línea numerada. 3. Predice: ¿Qué ocurre si el archivo no existe? Añade manejo de FileNotFoundError para mostrar un mensaje claro.

Ejercicio guiado 2: Escribir un CSV de notas.

1. Pide al usuario tres pares de nombre y nota. 2. Guárdalos en un archivo notas.csv con cabecera nombre,nota. 3. Usa csv.writer y newline="". 4. Vuelve a leer el archivo con csv.DictReader y muestra cada fila.

Ejercicio guiado 3: Guardar y cargar JSON.

1. Crea un diccionario con tu nombre, edad e intereses. 2. Guárdalo en perfil.json con json.dump y ensure_ascii=False. 3. Ábrelo con un editor de texto y observa el formato. 4. Carga el JSON con json.load y muestra el diccionario resultante.


13. Ejercicios autónomos

1. Predicción: ¿Qué ocurre si abres un archivo en modo "w" que ya contenía datos, sin escribir nada? ¿Y si lo abres en modo "a"? 2. pathlib: Construye una ruta que apunte a documentos/proyectos/curso/ejercicio.txt dentro del directorio personal del usuario, usando Path.home(). Crea los directorios necesarios con mkdir(parents=True, exist_ok=True) y escribe una línea en el archivo. 3. CSV: Lee un archivo CSV con columnas producto,precio,cantidad y calcula el valor total (precio * cantidad) de cada fila. Muestra el resultado y el total general. 4. JSON: Crea una lista de diccionarios (por ejemplo, contactos) y guárdala en JSON. Luego cárgala, añade un contacto nuevo y vuelve a guardarla. 5. Reto opcional: Investiga el nuevo método reorganize() de shelve en Python 3.15. Escribe un pequeño programa que cree un shelve, inserte diez entradas, elimine cinco y llame a reorganize(). Comprueba el tamaño del archivo antes y después.


14. Soluciones razonadas

Solución al ejercicio guiado 1:

from pathlib import Path
 
def mostrar_archivo(ruta):
    ruta = Path(ruta)
    try:
        with open(ruta, "r", encoding="utf-8") as f:
            for numero, linea in enumerate(f, start=1):
                print(f"{numero}: {linea.rstrip()}")
    except FileNotFoundError:
        print(f"No se encontró el archivo: {ruta}")
 
mostrar_archivo("notas.txt")

enumerate(f, start=1) numera las líneas desde 1. rstrip() elimina el salto de línea. FileNotFoundError se maneja con un mensaje claro.

Solución al ejercicio guiado 2:

import csv
 
datos = []
for _ in range(3):
    nombre = input("Nombre: ")
    nota = float(input("Nota: "))
    datos.append({"nombre": nombre, "nota": nota})
 
with open("notas.csv", "w", newline="", encoding="utf-8") as f:
    escritor = csv.DictWriter(f, fieldnames=["nombre", "nota"])
    escritor.writeheader()
    escritor.writerows(datos)
 
with open("notas.csv", newline="", encoding="utf-8") as f:
    for fila in csv.DictReader(f):
        print(fila)

csv.DictWriter escribe diccionarios usando las claves como columnas. writeheader() escribe la cabecera. writerows() escribe todas las filas de una vez.

Solución al ejercicio 3 (JSON):

import json
 
perfil = {
    "nombre": "Ana",
    "edad": 25,
    "intereses": ["Python", "ajedrez"]
}
 
with open("perfil.json", "w", encoding="utf-8") as f:
    json.dump(perfil, f, ensure_ascii=False, indent=2)
 
with open("perfil.json", "r", encoding="utf-8") as f:
    perfil_cargado = json.load(f)
 
print(perfil_cargado)

Solución al ejercicio 1 (predicción): Abrir en modo "w" vacía el archivo inmediatamente, aunque no escribas nada. Abrir en modo "a" no vacía el archivo; si no escribes, queda igual.

Solución al reto opcional (orientativa):

import shelve
import os
 
with shelve.open("prueba.db") as db:
    for i in range(10):
        db[f"clave_{i}"] = i
    for i in range(5):
        del db[f"clave_{i}"]
 
tam_antes = os.path.getsize("prueba.db")
with shelve.open("prueba.db") as db:
    db.reorganize()
tam_despues = os.path.getsize("prueba.db")
 
print(f"Antes: {tam_antes} bytes, después: {tam_despues} bytes")

El tamaño debería reducirse después de reorganize(), aunque el resultado exacto depende del backend de dbm.


15. Resumen

- La persistencia permite que los datos sobrevivan entre ejecuciones; los archivos son el mecanismo más directo. - pathlib.Path construye rutas portables entre Windows y Linux; el operador / une segmentos. - open() abre archivos en modos "r", "w", "a" o "x"; "w" vacía el archivo, "a" añade al final. - with garantiza el cierre del archivo incluso si hay errores. - Itera sobre el archivo (for linea in f) para procesar archivos grandes sin cargarlos en memoria. - Python 3.15 usa UTF-8 por defecto para la entrada/salida de texto, eliminando diferencias entre sistemas. Aun así, especifica encoding="utf-8" explícitamente. - csv lee y escribe datos tabulares; newline="" es importante en Windows. - json convierte entre objetos Python y texto JSON; ensure_ascii=False preserva caracteres no ASCII. - shelve ofrece un diccionario persistente; en Python 3.15 incorpora reorganize() para compactar el archivo. - Maneja FileNotFoundError, PermissionError y UnicodeDecodeError de forma específica.


16. Autoevaluación

1. ¿Por qué es importante usar with al abrir archivos? ¿Qué ocurre si no lo haces? 2. ¿Qué diferencia hay entre abrir un archivo en modo "w" y en modo "a"? 3. ¿Cómo construirías una ruta portable que apunte a datos/registros.txt dentro del directorio personal del usuario? 4. ¿Qué hace newline="" al trabajar con CSV en Windows y por qué es necesario? 5. Reto: Sin ejecutar, predice qué ocurre si abres un archivo en modo "r" que no existe. ¿Qué excepción se lanza? ¿Cómo lo manejarías?


17. Fuentes oficiales

- Tutorial oficial: Reading and Writing Files: https://docs.python.org/3.15/tutorial/inputoutput.html#reading-and-writing-files - Biblioteca estándar: pathlib — Object-oriented filesystem paths: https://docs.python.org/3.15/library/pathlib.html - Biblioteca estándar: csv — CSV File Reading and Writing: https://docs.python.org/3.15/library/csv.html - Biblioteca estándar: json — JSON encoder and decoder: https://docs.python.org/3.15/library/json.html - Biblioteca estándar: shelve — Python object persistence: https://docs.python.org/3.15/library/shelve.html - Novedades de Python 3.15 (UTF-8 por defecto, PEP 686; shelve.reorganize()): https://docs.python.org/3/whatsnew/3.15.html - PEP 686 – Make UTF-8 mode default: https://peps.python.org/pep-0686/ - PEP 8 – Style Guide for Python Code: https://peps.python.org/pep-0008/

informatica/programacion/cursos/programacion_desde_cero_con_python/archivos_y_persistencia.1793474652.txt.gz · Última modificación: por tempwin