Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Lectura y Escritura (Muelle de Carga)

El engranaje de escritura mueve los datos de la MEMORIA al DISCO

Fig. 11.3: La escritura mueve datos de la memoria al disco.

La RAM es volátil: apaga la computadora y todo lo que tenías en memoria desaparece. Para que los datos sobrevivan, tienen que salir de ese espacio efímero y escribirse en el disco (Fig. 11.3). Eso pasa en el Muelle de Carga.

Operaciones Rápidas (Carga Ligera)

pathlib tiene un método exprés para cuando solo quieres guardar o leer texto sin complicaciones. Dos líneas y listo.

from pathlib import Path

archivo = Path("nota_rapida.txt")

# ESCRIBIR (Sobrescribe todo si ya existe)
archivo.write_text("Compra leche", encoding="utf-8")

# LEER (Trae todo el contenido a memoria)
contenido = archivo.read_text(encoding="utf-8")
print(contenido)

(En la edición web ese archivo vive en la memoria del navegador, no en tu disco, y se esfuma al cerrar la pestaña; para verlo en tu carpeta real, corre el código en tu terminal.)

Nota: ese encoding="utf-8" protege tus eñes y acentos. Es tan importante que tiene su propia sección unas líneas más abajo.

Operaciones Profesionales (with open)

Agregar líneas a un registro sin borrar lo anterior requiere más control del que .write_text() puede darte. Para eso existe el Gestor de Contexto (context manager). Piensa en with así: abres la puerta del muelle, haces el trabajo y la puerta se cierra sola al terminar, sin que tengas que recordarlo.

from pathlib import Path

ruta_log = Path("registro.log")

# 'a' = Append (Agregar al final)
# 'w' = Write (Sobrescribir desde cero)
# 'r' = Read (Solo leer)

with open(ruta_log, mode="a", encoding="utf-8") as f:
    f.write("Evento 1: Inicio del sistema\n")
    f.write("Evento 2: Error en sector 7G\n")

# Al salir del bloque 'with', el archivo ya está cerrado y seguro.

with es el mismo try/finally del Capítulo 10, con su archivo.close() garantizado, ahora empaquetado: pase lo que pase dentro del bloque (incluso un error a media escritura), la puerta del muelle se cierra. Por eso es la forma idiomática: mismo seguro, cero ceremonia.

El Idioma de las Etiquetas (Encoding)

Ese encoding="utf-8" que venimos arrastrando merece su explicación, porque en un almacén que habla español no es opcional.

Un archivo no guarda letras: guarda números, y el encoding es el diccionario que traduce entre ambos. El problema es que existe más de un diccionario. UTF-8 es el estándar moderno que cubre todos los idiomas; pero Windows, por herencia, todavía usa otro (cp1252) por defecto en muchos casos. ¿Y qué pasa cuando se escribe con un diccionario y se lee con otro? Esto:

Se guardó:  "Añejo, 25 baterías"
Se leyó:    "Añejo, 25 baterÃas"

Ese ñ delator es el bug de encoding clásico, y el público de este libro (con sus eñes y sus acentos) lo va a encontrar tarde o temprano. La regla es una sola: escribe encoding="utf-8" en CADA open(), read_text() y write_text(), al guardar y al leer. Los dos lados de la conversación usan el mismo diccionario y el problema deja de existir. Por eso todos los ejemplos del libro lo llevan, y por eso el tuyo también debería.

Leer Línea por Línea

read_text() trae el archivo COMPLETO a memoria. Para una nota está bien; para un registro de miles de eventos es cargar el camión entero cuando solo querías revisar paquete por paquete. Un archivo abierto se puede recorrer directo con for, línea por línea (retomamos el registro.log que el bloque de with open dejó escrito):

from pathlib import Path

ruta_log = Path("registro.log")

with open(ruta_log, encoding="utf-8") as f:
    for linea in f:
        print(linea.strip())   # strip: fuera el salto de línea final

Cada vuelta entrega una línea y ya. La memoria solo carga una a la vez, sin importar el tamaño del archivo. El .strip() es costumbre sana: cada línea llega con su \n invisible al final, y limpiarlo te evita dobles saltos y comparaciones que fallan.

Cuando el Archivo No Está (Integración con el Cap. 10)

Los archivos son el primer territorio verdaderamente hostil que pisas: dependen del disco, del usuario, del sistema. El FileNotFoundError que el Capítulo 10 te enseñó a atrapar deja de ser teoría aquí:

try:
    contenido = Path("inventario.txt").read_text(encoding="utf-8")
except FileNotFoundError:
    print("Primer arranque: el registro aún no existe. Empezamos de cero.")
    contenido = ""

Fíjate en la decisión, no solo en la sintaxis: el archivo ausente no siempre es un error. En un primer arranque es lo esperado, y el programa responde con un valor por defecto en vez de morir. Es la misma decisión que toma cargar_datos en el proyecto final (ahí con ruta.exists(), la alternativa preventiva: pregunta antes de abrir en lugar de atrapar el golpe). Las dos formas valen; conocer ambas te deja elegir.

Leyendo Líneas con Formato

Los archivos de inventario reales no traen prosa: traen datos separados por comas. Una línea como "0001,Tornillos,5.50,100" es texto plano hasta que tú la cortas. Para eso está .split(","), las tijeras que conociste en el Capítulo 4.

linea: str = "0001,Tornillos,5.50,100"

partes: list[str] = linea.split(",")
# ["0001", "Tornillos", "5.50", "100"]  <- todo sigue siendo texto

codigo: str = partes[0]
nombre: str = partes[1]
precio: float = float(partes[2])  # Casting: de "5.50" a 5.50
cantidad: int = int(partes[3])    # Casting: de "100" a 100

print(f"{nombre}: {cantidad} unidades a ${precio}")

Observa el patrón: cortar con .split(), luego convertir cada pieza al tipo que le corresponde. Vale la pena dominarlo porque media internet guarda datos en archivos de texto con separadores. Para el proyecto final elegiremos otra ruta: JSON, que reconstruye los tipos solo y te ahorra toda esta capa de traducción. Lo ves al final de este capítulo.