Saltar al contenido

lección 4

Leyendo y escribiendo archivos

Tu programa toca el mundo exterior: leer CSVs, escribir resultados, trabajar con JSON. El I/O básico que todo profesional de datos necesita.

55 min

Hasta ahora, tus datos vivían dentro del script — hardcoded en variables. Pero en el mundo real, los datos viven en ARCHIVOS: CSVs que exporta el equipo de finanzas, JSONs que devuelve una API, logs de texto que genera un servidor. Tu trabajo como profesional de datos es LEER esos archivos, transformarlos y ESCRIBIR los resultados. Hoy aprendes el I/O (Input/Output) fundamental.

Piensa en esto: casi cualquier proceso de datos empieza leyendo un archivo de una carpeta, lo procesa y escribe el resultado en otra. Es como una fábrica — materia prima entra, producto terminado sale. Python tiene herramientas integradas para hacer exactamente eso, sin instalar nada extra.

### open() y with: la puerta al sistema de archivos

La función open() es tu punto de entrada al sistema de archivos. Abre un archivo y te devuelve un "manejador" que puedes leer o escribir. La forma correcta de usar open() es SIEMPRE con la sentencia "with" — que garantiza que el archivo se cierra automáticamente, incluso si hay un error.

1# Escribir un archivo de texto
2with open("datos_ejemplo.txt", "w", encoding="utf-8") as f:
3 f.write("producto,cantidad,precio\n")
4 f.write("Laptop,5,1299.99\n")
5 f.write("Monitor,12,349.50\n")
6 f.write("Teclado,30,79.99\n")
7
8print("Archivo creado.")
9
10# Leer el archivo completo
11with open("datos_ejemplo.txt", "r", encoding="utf-8") as f:
12 contenido = f.read()
13print(contenido)
14
15# Leer línea por línea (mejor para archivos grandes)
16with open("datos_ejemplo.txt", "r", encoding="utf-8") as f:
17 for linea in f:
18 print(linea.strip()) # strip() quita el\n del final

with open() es el patrón estándar. "w" escribe, "r" lee, "a" añade al final.

SIEMPRE especifica encoding="utf-8" al abrir archivos. Sin esto, en Windows Python usa una codificación diferente (cp1252) y los caracteres especiales (acentos, ñ, €) se corrompen. Este bug es invisible hasta que explota en producción con datos reales.

Y para contar líneas sin recorrerlas una a una, readlines() te devuelve la lista entera de golpe:

1with open("datos_ejemplo.txt", "r", encoding="utf-8") as f:
2 lineas = f.readlines() # lista con una entrada por línea
3 print(f"El fichero tiene {len(lineas)} líneas")

readlines() carga todas las líneas en una lista. len() de esa lista = número de líneas.

### Leer CSVs con el módulo csv

CSV (Comma-Separated Values) es el formato más común de intercambio de datos simple. Excel lo exporta, bases de datos lo generan, APIs lo devuelven. Python tiene un módulo csv integrado que maneja correctamente los casos problemáticos: comas dentro de valores entrecomillados, saltos de línea, caracteres especiales.

1import csv
2
3# Primero creamos un CSV de ejemplo
4with open("ventas.csv", "w", encoding="utf-8", newline="") as f:
5 writer = csv.writer(f)
6 writer.writerow(["fecha", "producto", "cantidad", "precio"])
7 writer.writerow(["2024-01-15", "Laptop Pro", 2, 1299.99])
8 writer.writerow(["2024-01-15", "Monitor 4K", 1, 449.00])
9 writer.writerow(["2024-01-16", "Teclado mecánico", 5, 89.99])
10 writer.writerow(["2024-01-16", "Ratón ergonómico", 3, 59.99])
11
12# Leer el CSV como lista de diccionarios (DictReader)
13with open("ventas.csv", "r", encoding="utf-8") as f:
14 reader = csv.DictReader(f)
15 ventas = list(reader)
16
17# Ahora ventas es una lista de diccionarios!
18for venta in ventas:
19 total = int(venta["cantidad"]) * float(venta["precio"])
20 print(f"{venta['fecha']} | {venta['producto']:20s} | {total:.2f}€")

csv.DictReader convierte un CSV directamente en lista de diccionarios. Perfecto.

Fíjate en algo importante: cuando lees con DictReader, TODOS los valores son strings. "cantidad" no es un int, es "2" como texto. Necesitas convertir explícitamente: int(venta["cantidad"]), float(venta["precio"]). Este es un error clásico de principiante — intentar sumar strings y obtener concatenación en vez de aritmética.

Y la otra mitad: escribir. La misma librería csv tiene un DictWriter que hace exactamente lo inverso — convierte una lista de diccionarios en un CSV, con una línea por registro:

1import csv
2
3filas = [
4 {"producto": "Laptop", "total": "3899.97"},
5 {"producto": "Monitor", "total": "1995.00"},
6]
7
8with open("salida.csv", "w", encoding="utf-8", newline="") as f:
9 writer = csv.DictWriter(f, fieldnames=["producto", "total"])
10 writer.writeheader() # escribe la fila de nombres
11 writer.writerows(filas) # escribe todas las filas de golpe

DictWriter es el inverso de DictReader: convierte diccionarios en filas de un CSV.

El CSV que te va a llegar de verdad: el de Excel en español. Todo lo de arriba vale para un CSV "de libro": separado por comas y sin sorpresas. Pero el primer CSV que te van a pasar en una empresa española sale de un Excel en español, y ése viene distinto: separa con punto y coma (;) en vez de coma, porque la coma la usa para los decimales. Escribe los decimales con coma: 1299,99 en vez de 1299.99. Y si se guardó como "CSV UTF-8", empieza con una marca invisible que se pega a la primera cabecera. Si lo abres sin decir nada, las columnas se funden en una sola y Python te dirá que no encuentra una columna que tú estás viendo.

1import csv
2
3# utf-8-sig se come la marca invisible; delimiter le dice cuál es el separador de verdad
4with open("ventas_excel.csv", "w", encoding="utf-8-sig", newline="") as f:
5 f.write("producto;cantidad;precio\n")
6 f.write("Laptop;3;1299,99\n")
7 f.write("Monitor;2;449,00\n")
8
9with open("ventas_excel.csv", "r", encoding="utf-8-sig", newline="") as f:
10 lector = csv.DictReader(f, delimiter=";")
11 for fila in lector:
12 # el decimal, de coma a punto, antes de convertirlo a número
13 precio = float(fila["precio"].replace(",", "."))
14 print(f"{fila['producto']}: {precio:.2f}€")

Truco: si al leer un CSV te sale UNA sola columna con todo dentro, el separador no es el que crees. Ábrelo con un editor de texto y míralo.

### JSON: el formato de las APIs

JSON (JavaScript Object Notation) es el formato universal de intercambio de datos en la web. Cada API devuelve JSON. Cada configuración moderna usa JSON. Y resulta que JSON se mapea DIRECTAMENTE a listas y diccionarios de Python. Es como si estuvieran hechos el uno para el otro.

1import json
2
3# Datos que queremos guardar
4resumen_diario = {
5 "fecha": "2024-01-15",
6 "total_ventas": 3057.96,
7 "num_transacciones": 4,
8 "productos_top": ["Laptop Pro", "Monitor 4K"],
9 "meta_alcanzada": True,
10}
11
12# Escribir JSON (serializar)
13with open("resumen.json", "w", encoding="utf-8") as f:
14 json.dump(resumen_diario, f, indent=2, ensure_ascii=False)
15
16print("JSON guardado.")
17
18# Leer JSON (deserializar)
19with open("resumen.json", "r", encoding="utf-8") as f:
20 datos = json.load(f)
21
22print(f"Fecha: {datos['fecha']}")
23print(f"Total: {datos['total_ventas']}€")
24print(f"Top: {datos['productos_top']}")

json.dump() escribe y json.load() lee. ensure_ascii=False para acentos.

Consejo de senior: usa indent=2 al escribir JSON durante desarrollo — lo hace legible. En producción, quita el indent para ahorrar espacio (cada archivo puede tener millones de registros). La diferencia de tamaño es significativa a escala.

### Rutas de archivos: el dolor silencioso

Las rutas de archivos son diferentes en Windows y Mac. Windows usa barras invertidas (C:\Users\datos\) y Mac usa barras normales (/Users/datos/). Para evitar problemas, Python tiene el módulo pathlib que maneja rutas de forma independiente del sistema operativo.

1from pathlib import Path
2
3# pathlib funciona igual en Windows y Mac
4ruta_datos = Path("data") / "raw" / "ventas.csv"
5print(ruta_datos) # data/raw/ventas.csv (Mac) o data\raw\ventas.csv (Win)
6
7# Crear directorios si no existen
8ruta_datos.parent.mkdir(parents=True, exist_ok=True)
9
10# Verificar si existe
11if ruta_datos.exists():
12 print(f"Tamaño: {ruta_datos.stat().st_size} bytes")
13else:
14 print("El archivo no existe aún")
15
16# Listar archivos de un directorio
17for archivo in Path("data").glob("*.csv"):
18 print(f"Encontrado: {archivo.name}")

pathlib es la forma moderna y multiplataforma de manejar rutas.

Nunca concatenes rutas con strings ("data/" + "archivo.csv"). Usa pathlib o os.path.join(). Las barras son diferentes en cada SO y los errores son sutiles — tu script funciona en tu Mac pero falla en el servidor Windows del compañero.

Este patrón Leer-Transformar-Escribir es la base de todo el trabajo con datos.

Regístrate para guardar tu progreso.

## comentarios

Reporta erratas, ayuda a otros o comparte tu opinión. Sé constructivo.

Inicia sesión para comentar y responder.

cargando comentarios...