Tiza Paso a paso
Practicar →

Pensamiento Computacional · Unidad 5 — Entrada, salida y errores

Archivos

Un archivo es la forma de que los datos sobrevivan al programa. Se abre, se usa y se cierra — y lo que se lee siempre es texto.

Practicar este tema →8 ejercicios que te corrigen paso a paso · gratis, sin cuenta

La explicación

Por qué hacen falta

Las variables viven en memoria y desaparecen cuando el programa termina. Si querés que algo quede, tiene que ir al disco. Eso es un archivo.

Abrir, usar, cerrar

f = open("datos.txt", "r")
contenido = f.read()
f.close()

El segundo argumento es el modo:

"r"   leer         error si el archivo no existe
"w"   escribir     borra todo lo que había
"a"   agregar      escribe al final, conserva lo anterior
Cuidado con "w". Abrir en modo escritura vacía el archivo al instante, antes incluso de que escribas nada. Si querías agregar y pusiste "w", perdiste el contenido y no hay forma de recuperarlo. Cuando dudes entre "w" y "a", pensá si querés empezar de cero o sumar.

La forma recomendada: with

with open("datos.txt", "r") as f:
    contenido = f.read()
# acá afuera el archivo ya está cerrado

Cierra solo, incluso si adentro se produce un error. Con open y close sueltos, un error en el medio deja el archivo abierto. Usá siempre with.

Tres formas de leer

f.read()       → todo el contenido como un solo string
f.readlines()  → una lista con todas las líneas
for linea in f:  → de a una, sin cargar todo en memoria

La tercera es la que conviene con archivos grandes: no necesita que todo entre en memoria.

Dos cosas que siempre sorprenden al leer

1. Cada línea trae un salto al final. Si el archivo dice 25, lo que leés es "25\n". Por eso se usa .strip(), que saca espacios y saltos de los bordes. Dónde molesta ese salto: en las comparaciones. linea == "25" da False y linea.strip() == "25" da True. A int() en cambio no le molesta —int("25\n") devuelve 25 igual—, pero conviene hacer strip() siempre y no depender de esa tolerancia.

2. Todo lo que se lee es texto. Igual que con input(): si es un número, hay que convertirlo con int() o float().
with open("edades.txt") as f:
    for linea in f:
        edad = int(linea.strip())   # las dos cosas

Escribir

with open("salida.txt", "w") as f:
    f.write("primera línea\n")
    f.write("segunda línea\n")

write no agrega el salto de línea: si no lo ponés vos, queda todo pegado en una sola línea. Y sólo escribe strings, así que los números van con str().

Un ejercicio resuelto, paso a paso

Un archivo notas.txt tiene una nota por línea. Escribir una función que devuelva el promedio.

def promedio(nombre_archivo):
    total = 0
    cantidad = 0
    with open(nombre_archivo, "r") as f:
        for linea in f:
            total = total + int(linea.strip())
            cantidad = cantidad + 1
    return total / cantidad

Las tres cosas que hay que notar:

  1. Los acumuladores van antes del with. Si los pusieras adentro del for se reiniciarían en cada línea — el mismo error del tema 3.3.
  2. .strip() antes de int(). Ojo con lo que se dice de esto: int("7\n") no falla — int() descarta los espacios y los saltos de los bordes y devuelve 7. Lo que sí falla es cualquier otra cosa pegada al número: int(" 7 años "), int("7.0"), int(""). El strip() va igual, porque el salto sí rompe cualquier comparación de texto (linea == "7" da False, linea.strip() == "7" da True) y porque en cuanto el archivo trae algo más que el número limpio, hace falta de verdad.
  3. with cierra solo. No hace falta f.close().
Esta función tiene un agujero: si el archivo está vacío, cantidad queda en 0 y total / cantidad tira ZeroDivisionError. La forma prolija:
if cantidad == 0:
    return 0   # o None, según qué tenga sentido
return total / cantidad
Los archivos vacíos existen, y son el caso que nadie prueba.

Control: el promedio tiene que quedar entre la nota más baja y la más alta del archivo. Si te da fuera de ese rango, revisá el acumulador.

Y uno del nivel del parcial

Un archivo ventas.txt tiene una venta por línea, con el formato producto,cantidad,precio. Escribir una función que devuelva el total facturado y el producto que más recaudó, ignorando las líneas vacías y los espacios sobrantes.

Un archivo de texto real nunca viene prolijo. Va a tener líneas en blanco al final, espacios de más, y un salto de línea al final de cada renglón. El ejercicio pide justamente eso, así que la limpieza es parte de la respuesta.

El archivo de prueba:

lapicera,10,150
(línea vacía)
cuaderno,3,800
lapicera,5,150
  goma,20,50 

La función:

def leer_ventas(ruta):
    total = 0.0
    por_producto = {}
    with open(ruta, encoding="utf-8") as f:
        for linea in f:
            linea = linea.strip()
            if not linea:
                continue
            prod, cant, precio = linea.split(",")
            monto = int(cant) * float(precio)
            total = total + monto
            por_producto[prod] = por_producto.get(prod, 0) + monto
    mejor = max(por_producto, key=lambda p: por_producto[p])
    return (total, mejor, por_producto[mejor])

El with open(...) es la forma correcta de abrir un archivo: lo cierra solo al terminar, aunque el programa falle en el medio. Con open pelado hay que acordarse del close(), y si salta un error el archivo queda abierto.

El encoding="utf-8" evita que el programa se rompa con acentos y eñes. Sin él, el comportamiento depende del sistema operativo: anda en una máquina y falla en otra.

El .strip() hace dos trabajos a la vez: saca el salto de línea final —sin eso, la línea en blanco llega como un salto solo, que no cuenta como vacía, y el if de abajo no la saltea— y saca los espacios de la línea de la goma, que si no quedaría guardada con la clave " goma". Al precio el salto no le molesta: float() lo tolera, igual que int().

El if not linea: continue saltea las líneas vacías. Sin eso, linea.split(",") devuelve una lista de un elemento y el desempaquetado en tres variables tira un error.

Los resultados, con el archivo de prueba:

total = 10·150 + 3·800 + 5·150 + 20·50
      = 1500 + 2400 + 750 + 1000 = 5650.0

por producto:  lapicera 2250,  cuaderno 2400,  goma 1000
el que más recaudó: cuaderno, con 2400

Fijate el detalle que el ejercicio esconde: la lapicera aparece en dos líneas y hay que sumarlas. Si el diccionario se llenara con por_producto[prod] = monto en vez de sumar, la segunda línea pisaría a la primera y la lapicera quedaría en 750.

Y otro detalle: la lapicera se vendió más veces (15 unidades contra 3), pero el cuaderno recaudó más. La pregunta es cuál recaudó más, no cuál se vendió más — y esas dos respuestas casi nunca coinciden.

El control de la suma: el total tiene que ser igual a la suma de los tres montos por producto: 2250 + 2400 + 1000 = 5650 ✓. Ese control detecta si alguna línea se perdió o se contó dos veces.

Por qué int(cant) y float(precio). Las cantidades son enteras (no se venden 2,5 lapiceras) y los precios pueden tener centavos. Elegir el tipo según lo que representa el dato es parte de escribir bien.

Dónde se pierde el punto: no hacer el strip(). El error que aparece es "not enough values to unpack (expected 3, got 1)" en la línea del split, por la línea en blanco que no se salteó, y señala el desempaquetado, que no es donde está el problema.

Dónde se cae la mayoría

Hasta acá la explicación. La práctica es la otra mitad: 8 ejercicios de este tema, en cuatro niveles, que no te dicen sólo si está bien al final sino que te corrigen en cada paso y te explican por qué. Y uno para escribir el programa vos, que se prueba ejecutándolo en Python ahí mismo, en el navegador. Además trae una animación que muestra la idea en movimiento.

Practicar Archivos →

Dónde entra este tema en el parcial

Los otros temas de la unidad 5

Entrada, salida y errores

Ver todos los temas de Pensamiento Computacional