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 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.
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 csv23# utf-8-sig se come la marca invisible; delimiter le dice cuál es el separador de verdad4with 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")89with 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úmero13 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 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.
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...