Skip to content

📙 Clase 21 — CSV: tablas para Excel

Fase 4 · Archivos y el mundo exterior ⬅️ Volver al índice de clases

🎯 Qué aprendí

  • Leer y escribir CSV (valores separados por comas): el formato de las tablas.
  • DictWriter/DictReader: CSV ↔ lista de diccionarios, directo.
  • El botón "Exportar a Excel" de tu app, resuelto.

📖 PARTE TEÓRICA

📊 1. ¿Qué es un CSV?

Texto plano donde cada línea es una fila y las columnas van separadas por comas. Excel, Google Sheets y cualquier sistema lo abren directo.

csv
nombre,tel
Ana,999-111
Luis,988-222
JSONCSV
Formaestructuras anidadastabla plana (filas × columnas)
Lo abre un humano eneditor de textoExcel / Sheets
Ideal paraconfig, datos de la appexportar/importar tablas

✍️ 2. Escribir: DictWriter (desde lista de dicts)

Tu formato de siempre — lista de diccionarios — sale a CSV así:

python
import csv

contactos = [
    {"nombre": "Ana", "tel": "999-111"},
    {"nombre": "Luis", "tel": "988-222"},
]

with open("contactos.csv", "w", newline="", encoding="utf-8") as f:
    writer = csv.DictWriter(f, fieldnames=["nombre", "tel"])   # columnas y su orden
    writer.writeheader()                                        # fila de títulos
    writer.writerows(contactos)                                 # todas las filas

⚠️ El newline="" al abrir es obligatorio con csv (evita filas en blanco intercaladas en Windows). Es el gotcha #1 del módulo.

📖 3. Leer: DictReader (a lista de dicts)

python
with open("contactos.csv", newline="", encoding="utf-8") as f:
    for fila in csv.DictReader(f):        # usa la 1ª línea como claves
        print(dict(fila))
# {'nombre': 'Ana', 'tel': '999-111'}
# {'nombre': 'Luis', 'tel': '988-222'}

⚠️ Todo llega como texto: si guardaste edad=30, vuelve "30". Convierte al leer: int(fila["edad"]).

🧪 Tip de entrevista: "¿Por qué DictReader/DictWriter en vez de reader/writer?" → Trabajan con diccionarios (columnas por nombre) en vez de listas (por posición): el código no se rompe si cambia el orden de las columnas.


🖥️ EN TU APP DE ESCRITORIO

El clásico botón "Exportar" — tus datos en manos del usuario, en el formato que su jefe entiende:

python
from tkinter import filedialog          # diálogo nativo "Guardar como…"
import csv

class App(ctk.CTk):
    def _exportar_csv(self):
        ruta = filedialog.asksaveasfilename(
            defaultextension=".csv",
            filetypes=[("CSV (Excel)", "*.csv")],
        )
        if not ruta:                     # el usuario canceló
            return
        with open(ruta, "w", newline="", encoding="utf-8") as f:
            writer = csv.DictWriter(f, fieldnames=["texto", "hecha"])
            writer.writeheader()
            writer.writerows(self._tareas)
        self.status.configure(text=f"✅ Exportado a {ruta}")

Y el espejo, "Importar": filedialog.askopenfilename() + DictReader + repintar.

💡 El combo filedialog + CSV es de lo más pedido en apps de escritorio reales: reportes para contabilidad, respaldo de datos, migración entre sistemas. Los diálogos los vemos a fondo en la Fase 6.


🗄️ CON BASE DE DATOS (caso de uso)

CSV es el idioma de intercambio de las bases de datos: exportar una tabla para Excel, o importar un CSV que te pasaron hacia tu SQLite:

python
# SELECT → CSV (reporte)
filas = con.execute("SELECT nombre, tel FROM contactos").fetchall()
with open("reporte.csv", "w", newline="", encoding="utf-8") as f:
    writer = csv.writer(f)
    writer.writerow(["nombre", "tel"])       # cabecera
    writer.writerows(filas)                  # las tuplas de la BD, directo

# CSV → INSERT (importación)
with open("nuevos.csv", newline="", encoding="utf-8") as f:
    for fila in csv.DictReader(f):
        con.execute("INSERT INTO contactos VALUES (:nombre, :tel)", fila)
con.commit()

🗄️ Fíjate: DictReader produce dicts y el INSERT con :nombre los consume — el puente dict=fila de la Clase 04 une todos los formatos.


🏋️ EJERCICIOS CON SOLUCIÓN

Ejercicio 1 — Exportar gastos

Guarda en gastos.csv esta lista, con cabecera: [{"concepto": "luz", "monto": 120}, {"concepto": "agua", "monto": 45}]

Ver solución
python
import csv

gastos = [{"concepto": "luz", "monto": 120}, {"concepto": "agua", "monto": 45}]
with open("gastos.csv", "w", newline="", encoding="utf-8") as f:
    writer = csv.DictWriter(f, fieldnames=["concepto", "monto"])
    writer.writeheader()
    writer.writerows(gastos)

Ejercicio 2 — Leer y sumar

Lee gastos.csv y calcula el total (ojo: los montos llegan como texto).

Ver solución
python
import csv

with open("gastos.csv", newline="", encoding="utf-8") as f:
    total = sum(int(fila["monto"]) for fila in csv.DictReader(f))
print(total)    # 165   ← int() porque CSV entrega strings

Ejercicio 3 — Filtrar al importar

Lee gastos.csv y quédate solo con los gastos mayores a 100 (lista de dicts).

Ver solución
python
import csv

with open("gastos.csv", newline="", encoding="utf-8") as f:
    grandes = [dict(fila) for fila in csv.DictReader(f) if int(fila["monto"]) > 100]
print(grandes)    # [{'concepto': 'luz', 'monto': '120'}]

❓ Preguntas y respuestas (autoevaluación)

1. ¿Cuándo CSV y cuándo JSON?

CSV para tablas planas que abrirá Excel/otro sistema; JSON para estructuras anidadas y datos internos de la app.

2. ¿Qué parámetro es obligatorio al abrir archivos para csv y por qué?

newline="" — sin él pueden aparecer filas en blanco intercaladas (especialmente en Windows).

3. ¿De qué tipo llegan los valores al leer un CSV?

Todos string: hay que convertir (int(), float()) al leer.

4. ¿Qué ventaja da DictWriter sobre writer?

Columnas por nombre (dicts): el orden no importa y el código es autoexplicativo.


📎 Apuntes relacionados

➡️ Siguiente

Clase 22 · Librería estándardatetime, pathlib y random: las pilas incluidas.