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

Formatos de Datos: CSV y JSON (El Manifiesto de Carga)

Fig. 11.5: El mismo dato como CSV plano y JSON anidado.

Guardar texto suelto en un archivo está bien para notas. Pero el almacén maneja datos estructurados: productos con nombre, precio y stock. Una lista de registros donde todos comparten los mismos campos. Para eso, escribir y leer “a mano” se queda corto y, peor, es frágil. Python trae dos formatos estándar para esto, cada uno con su módulo.

CSV: la hoja de cálculo en texto plano

Un CSV (Comma-Separated Values) es una tabla: una línea por registro, columnas separadas por comas. Lo abre Excel, lo abre Google Sheets, lo abre todo.

En la sección anterior aprendiste a cortar líneas con .split(","), y ese patrón sigue valiendo oro para entender qué hay dentro de un archivo. Pero para producir o consumir CSV de verdad, resístete a armarlo a mano pegando texto con comas. El día que un nombre traiga una coma ("Tornillos, 2mm"), tu línea tendrá una columna de más y la lectura se romperá sin avisar. Ese problema ya está resuelto: el módulo csv se encarga de las comillas y los escapes por ti. En resumen: .split() para entender, csv para operar.

import csv

productos = [
    {"id": "1", "nombre": "Tornillos, 2mm", "precio": 5.5, "stock": 500},
    {"id": "2", "nombre": "Taladro",        "precio": 89.0, "stock": 12},
]

# Escribir: DictWriter mapea cada diccionario a una fila
with open("inventario.csv", "w", newline="", encoding="utf-8") as f:
    columnas = ["id", "nombre", "precio", "stock"]
    escritor = csv.DictWriter(f, fieldnames=columnas)
    escritor.writeheader()
    escritor.writerows(productos)

# Leer: DictReader devuelve cada fila como un diccionario
with open("inventario.csv", encoding="utf-8") as f:
    for fila in csv.DictReader(f):
        print(fila["nombre"])

DictWriter y DictReader hablan el mismo idioma que tus diccionarios, así que el código queda legible. Una advertencia que no cambia: del archivo todo sale como texto. fila["precio"] será "5.5" (una cadena), y tendrás que convertirlo con float() si vas a hacer cuentas.

(En la edición web ese inventario.csv no llega a tu disco: vive en la memoria del navegador y se pierde al cerrar la pestaña. Para guardarlo de verdad, ejecútalo en tu terminal.)

JSON: la estructura tal cual

CSV es plano: filas y columnas, nada anidado. Pero los datos de tu programa casi nunca son tan planos. JSON (JavaScript Object Notation) guarda la estructura exactamente como la tienes en memoria: una lista de diccionarios sigue siendo una lista de diccionarios al volver del disco, con sus números como números y sus textos como textos.

import json

productos = [
    {"id": "1", "nombre": "Tornillos, 2mm", "precio": 5.5, "stock": 500},
    {"id": "2", "nombre": "Taladro",        "precio": 89.0, "stock": 12},
]

# Escribir toda la estructura de un golpe
with open("inventario.json", "w", encoding="utf-8") as f:
    json.dump(productos, f, indent=2, ensure_ascii=False)

# Leer y recuperar la estructura idéntica
with open("inventario.json", encoding="utf-8") as f:
    cargados = json.load(f)

print(cargados[0]["precio"] + 1)   # 6.5  ← ya es número, sin convertir nada

Dos llamadas, json.dump y json.load, y el viaje de ida y vuelta no pierde nada: ni los tipos, ni los acentos legibles en el archivo (gracias a ensure_ascii=False), ni la forma anidada. Por eso no hay que “rearmar” cada registro a mano como con el CSV.

Cuidado: JSON guarda la estructura completa en una sola operación, no registro por registro. Escribe toda la lista de un json.dump, no llames a dump dentro de un bucle.

La Forma Anidada, Demostrada

“Anidado” no es palabra decorativa. Significa que un valor puede ser, a su vez, un diccionario o una lista, y JSON lo conserva a cualquier profundidad. Un producto con proveedor y ubicaciones:

import json

producto = {
    "nombre": "Tornillos",
    "proveedor": {"empresa": "Aceros MX", "ciudad": "Monterrey"},
    "ubicaciones": ["Pasillo A", "Pasillo C"],
}

texto = json.dumps(producto, ensure_ascii=False)   # a texto JSON
recuperado = json.loads(texto)                     # y de regreso

# El acceso encadenado navega nivel por nivel, de afuera hacia adentro:
print(recuperado["proveedor"]["ciudad"])    # Monterrey
print(recuperado["ubicaciones"][0])         # Pasillo A

Intenta meter ese proveedor en una celda de CSV y entenderás por qué existe JSON. La rejilla plana no tiene dónde ponerlo; la estructura anidada es su hábitat.

¡Ojo!: el viaje de regreso tiene su accidente propio. Si el archivo JSON está corrupto (una coma de más, una llave sin cerrar, una edición manual con mala suerte), json.load lanza un json.JSONDecodeError. Es un miembro más de la familia del Capítulo 10: atrapable con except, como cualquier otro. El proyecto final lo menciona de nuevo.

¿Cuál uso?

Cuándo usar CSVCuándo usar JSON
necesitas abrirlo en Excel o compartirlo como tablatus datos tienen estructura (listas de diccionarios, anidamiento)
los datos son una rejilla plana de filas y columnasquieres que los tipos (número, texto, booleano) sobrevivan al guardado

Para una lista de diccionarios, JSON viene como anillo al dedo: guardas y cargas con dos líneas y sin convertir tipos a mano.


Resumen del Capítulo

  • pathlib.Path construye rutas que funcionan en cualquier sistema operativo, y permite consultar (.exists(), .name, .suffix, .parent) y actuar (.mkdir(), .iterdir(), .glob(), .unlink(), .rmdir()) sobre el disco.
  • .write_text()/.read_text() son el atajo rápido para texto simple; with open(...) as f: da control total (modos "r", "w", "a") y cierra el archivo solo al terminar.
  • encoding="utf-8" es obligatorio en cada apertura de archivo, o los acentos y las eñes se corrompen al leer.
  • Un archivo abierto se puede recorrer línea por línea con for, sin cargarlo completo a memoria.
  • Un módulo se trae con import; la biblioteca estándar viene incluida con Python, y pip install trae paquetes externos de PyPI (siempre dentro de un venv).
  • CSV guarda tablas planas (csv.DictWriter/DictReader); JSON conserva estructuras anidadas y los tipos originales (json.dump/load), ideal para una lista de diccionarios.