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 ingeniero 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 ingeniero de datos es LEER esos archivos, transformarlos y ESCRIBIR los resultados. Hoy aprendes el I/O (Input/Output) fundamental.
Piensa en esto: un pipeline de datos típico empieza leyendo un archivo de un directorio, lo procesa y escribe el resultado en otro directorio. 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 texto2with 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")78print("Archivo creado.")910# Leer el archivo completo11with open("datos_ejemplo.txt", "r", encoding="utf-8") as f:12 contenido = f.read()13print(contenido)1415# 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ínea3 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 csv23# Primero creamos un CSV de ejemplo4with 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])1112# 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)1617# 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 csv23filas = [4 {"producto": "Laptop", "total": "3899.97"},5 {"producto": "Monitor", "total": "1995.00"},6]78with 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 nombres11 writer.writerows(filas) # escribe todas las filas de golpe
DictWriter es el inverso de DictReader: convierte diccionarios en filas de un CSV.
### 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 json23# Datos que queremos guardar4resumen_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}1112# Escribir JSON (serializar)13with open("resumen.json", "w", encoding="utf-8") as f:14 json.dump(resumen_diario, f, indent=2, ensure_ascii=False)1516print("JSON guardado.")1718# Leer JSON (deserializar)19with open("resumen.json", "r", encoding="utf-8") as f:20 datos = json.load(f)2122print(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 Path23# pathlib funciona igual en Windows y Mac4ruta_datos = Path("data") / "raw" / "ventas.csv"5print(ruta_datos) # data/raw/ventas.csv (Mac) o data\raw\ventas.csv (Win)67# Crear directorios si no existen8ruta_datos.parent.mkdir(parents=True, exist_ok=True)910# Verificar si existe11if ruta_datos.exists():12 print(f"Tamaño: {ruta_datos.stat().st_size} bytes")13else:14 print("El archivo no existe aún")1516# Listar archivos de un directorio17for 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.
## ejercicios
Leer CSV y calcular totales
Lee el CSV de entrada con DictReader, calcula el total de cada fila (cantidad × precio con 2 decimales) y escríbelo como CSV de salida.
💡 Resultado esperado
--- salida.csv --- producto,cantidad,precio,total Laptop,3,1299.99,3899.97 Monitor,5,399.00,1995.00
Configuración del pipeline en JSON
Guarda un diccionario de configuración en un fichero JSON, léelo de vuelta y úsalo para filtrar datos.
💡 Resultado esperado
--- config.json ---
{
"ruta_entrada": "data/ventas.csv",
"ruta_salida": "data/resultado.json",Procesar múltiples archivos
Busca todos los .txt en un directorio, cuenta las líneas de cada uno y muestra el total.
💡 Resultado esperado
Archivos encontrados: 3 server_1.txt: 10 líneas server_2.txt: 20 líneas server_3.txt: 30 líneas
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...