Lab guiado: Operaciones con ficheros en Python¶
- Unidad: UT2 — Ficheros
- Agrupamiento: parejas conductor/navegante
- Recursos:
- Ficheros de trabajo:
paso0.py,paso1.py, …paso8.py(uno por paso) - Ficheros de datos generados:
notas.txt,notas_nuevas.txt,registro.txt,informe.txt
Crearemos todos los ficheros
.pyy.txten la misma carpeta. Usa rutas relativas en todo el lab ("notas.txt", no rutas absolutas).
Cómo trabajar en parejas¶
Este lab se realiza en parejas conductor/navegante. Leed las instrucciones antes de empezar.
Roles¶
| Rol | Qué hace |
|---|---|
| Conductor | Escribe el código y ejecuta los scripts. Solo el conductor toca el teclado. |
| Navegante | Lee el enunciado en voz alta, propone soluciones, detecta errores y formula las preguntas. |
Rotación¶
- Cambiad de rol al final de cada paso (0, 1, 2… 8).
- Al rotar, el navegante ocupa el teclado y el conductor se convierte en navegante.
- Ambos debéis poder explicar cualquier parte del código al terminar el lab.
Entrega¶
Carpeta comprimida en forma zip que contenga os ficheros .py de cada paso: paso0.py, paso1.py, … paso8.py, más los de las ampliaciones que hayas completado.
Conceptos base¶
Antes de escribir código: repasemos los conceptos de los apuntes
¿Qué es un fichero? Una colección de datos guardada de forma persistente en disco. A diferencia de las variables, sobrevive al cierre del programa.
Texto vs binario
En este lab trabajamos solo con ficheros de texto (.txt). Los ficheros binarios (imágenes, PDFs...) se abren con modo "b" y se tratan de forma diferente.
Ruta relativa
Si el script y el fichero están en la misma carpeta, basta con escribir "notas.txt". Python lo busca en el directorio de trabajo actual.
open(ruta, mode, encoding)
La puerta de entrada a cualquier fichero. Devuelve un objeto fichero con métodos para leer o escribir.
with open(...) as f:
Garantiza que el fichero se cierra siempre al salir del bloque, aunque ocurra un error. Úsalo siempre.
encoding="utf-8"
Obligatorio para que las tildes y la ñ funcionen en cualquier equipo. Si lo omites, el comportamiento depende del sistema operativo.
Paso 0 — Crear el fichero de notas paso0.py¶
Conceptos
Modo "w": abre para escribir. Si el fichero ya existe, borra su contenido. Si no existe, lo crea.
with cierra el fichero automáticamente al salir del bloque.
Objetivo¶
Crear notas.txt con los datos de cuatro alumnos usando modo "w" y write().
Tarea¶
Inserta en paso0.py el siguiente programa:
with open("notas.txt", "w", encoding="utf-8") as f:
f.write("Ana García: 8.5\n")
f.write("Carlos López: 6.0\n")
f.write("María Fernández: 9.2\n")
f.write("Pedro Martín: 4.5\n")
print("Fichero creado.")
Pista
Cada llamada a write() escribe exactamente la cadena que le pasas. Si no añades \n, las líneas quedan pegadas en una sola.
Pregunta:¶
Si ejecutas el script dos veces seguidas sin modificarlo, ¿el fichero tendrá 8 líneas o 4?
Respuesta
4 líneas. El modo "w" borra el contenido previo cada vez que abre el fichero.
Ejecuta y verifica que notas.txt tiene 4 líneas, una por alumno
TODO¶
Modifica paso0.py de forma que obtengamos el mismo resultado completando el siguiente código
notas = [
("Ana García", 8.5),
("Carlos López", 6.0),
("María Fernández", 9.2),
("Pedro Martín", 4.5),
]
with open("notas.txt", "w", encoding="utf-8") as f:
# TODO
print("Fichero creado.")
Comprobación¶
Abre notas.txt con el editor de texto y verifica que tiene 4 líneas, una por alumno.
Reflexión¶
¿Qué hemos aprendido en este paso?
Respuestas
- A abrir un fichero en modo escritura (
"w") para crearlo o sobrescribirlo. - A usar
withpara cerrar el fichero automáticamente y evitar errores. - A escribir texto línea a línea con
write(). - A controlar los saltos de línea con
\npara que cada registro quede en una línea. - A entender que ejecutar de nuevo con
"w"no añade contenido: reemplaza el anterior.
Prueba manual guiada¶
- Ejecuta el script.
- Abre
notas.txtcon el editor. Cuenta las líneas. - Ejecuta el script de nuevo sin modificarlo.
- Vuelve a abrir
notas.txt. ¿Cuántas líneas hay ahora? - Confirma con tu pareja la respuesta a la pregunta anterior.
Paso 1 — Leer el fichero completo con read() paso1.py¶
Conceptos
read() carga todo el contenido del fichero en una única cadena str, incluyendo los saltos de línea \n. El modo de apertura es "r" (lectura, valor por defecto).
Objetivo¶
Leer el fichero completo y mostrar su contenido, tipo de dato y número de caracteres.
Tarea¶
Inserta en paso1.py el siguiente programa:
with open("notas.txt", "r", encoding="utf-8") as f:
contenido = f.read()
print(contenido)
print("---")
print(type(contenido))
print(f"Caracteres totales: {len(contenido)}")
Pista
type() te dice qué tipo de dato ha devuelto read(). len() cuenta los caracteres, incluidos los \n.
Pregunta:¶
¿Qué tipo de dato devuelve
read():str,listobytes?
Respuesta
str. read() devuelve una cadena de texto única con todo el contenido del fichero.
Ejecuta el script y verifica la salida en la terminal.
TODO¶
Edita el fichero paso1.py y agrega el siguiente código de forma que a partir del string ya leído en contenido, sin reabrir el fichero, construye un diccionario {nombre: nota} e imprime solo los alumnos aprobados (nota >= 5).
registro_alumnos = {}
for linea in ______: # itera sobre las líneas del string
# TODO: agrega a registro los datos de los alumnos
# TODO: muestra solo los alumnos aprobados recorriendo registro_alumnos
Comprobación¶
El programa principal muestra las 4 líneas, <class 'str'> y el total de caracteres. El TODO imprime solo los alumnos con nota >= 5.
Reflexión¶
¿Qué hemos aprendido en este paso?
Respuestas
read()carga todo el fichero en unstrde una sola vez; no hace falta reabrirlo para procesar los datos.- El
strse puede procesar como cualquier cadena:split,strip, conversiones de tipo, etc. - Es adecuado para ficheros pequeños donde se necesita el contenido completo de inmediato.
Paso 2 — Leer por partes: read(n) y readline() paso2.py¶
Conceptos
read(n): lee exactamentencaracteres y mueve el cursor interno. La siguiente llamada continúa desde donde se quedó.readline(): lee hasta el siguiente\n(inclusive). Devuelve""cuando llega al final del fichero.
Objetivo¶
Usar read(n) para leer fragmentos y readline() en bucle para recorrer el fichero completo.
Tarea¶
Inserta en paso2_a.py el siguiente programa:
Parte A — read(n):
paso2_b.py el siguiente programa:
Parte B — readline() en bucle:
Pista
repr() muestra los caracteres especiales de forma visible (verás los \n explícitamente). Muy útil para depurar.
🔧 Apoyo — solo si te atascas en la Parte B
Empieza rellenando los huecos de este template:
Pregunta:¶
En el bucle con
readline(), ¿qué valor tienelineacuando el fichero se ha leído por completo? ¿Por qué el bucle se detiene?
Respuesta
linea vale "" (cadena vacía). not "" es True, por lo que se ejecuta break y el bucle termina.
Ejecuta ambas partes y observa la salida.
TODO¶
Crea el fichero paso2.py cuyo código sera el de paso2_b.py modificándolo de forma que usando readline() en un bucle, se extraen solo los nombres de los alumnos (la parte antes de ": ") y guárdalos en una lista nombres. No uses readlines() ni for linea in f.
nombres = []
# TODO: leer del fichero líneas guardando solo nombres de los alumnos en la lista nombres
print(nombres)
Comprobación¶
- Parte A:
primerosysiguientesmuestran fragmentos del texto, no líneas completas. - Parte B: se imprimen las 4 líneas con
\nvisible al final; el bucle termina solo. - TODO:
nombreses una lista con los 4 nombres sin notas ni\n.
Reflexión¶
¿Qué hemos aprendido en este paso?
Respuestas
read(n)mueve un cursor interno; cada llamada lee el siguiente fragmento, no el mismo.readline()permite avanzar manualmente línea a línea;""indica fin de fichero.- La condición de parada del bucle
while Trueconreadline()es que devuelva cadena vacía.
Paso 3 — Leer todas las líneas con readlines() paso3.py¶
Conceptos
readlines() lee todo el fichero de una vez y devuelve una lista donde cada elemento es una línea (con \n incluido). Útil cuando necesitas acceder a líneas por índice.
writelines() escribe de una vez en un fichero una lista donde cada elemento es una línea de texto. No añade saltos de linea: si quieres que cada elemento quede en una linea distinta, cada string de la lista debe terminar en \n.
Objetivo¶
Leer el fichero con readlines() y mostrar la segunda y la última línea.
Tarea¶
Inserta en paso3.py el siguiente programa:
with open("notas.txt", "r", encoding="utf-8") as f:
lineas = f.readlines()
print(f"Total de líneas: {len(lineas)}")
print(f"Segunda línea: {lineas[1].strip()}")
print(f"Última línea: {lineas[-1].strip()}")
Pista
.strip() elimina espacios y saltos de línea al principio y al final de la cadena.
Pregunta:¶
¿Qué contiene
lineas[0]exactamente, incluyendo caracteres especiales?
Respuesta
'Ana García: 8.5\n' — la primera línea con el salto de línea incluido.
Ejecuta el script y verifica la salida.
TODO¶
Añade a paso3.py código siguiente completado de forma que se ordena la lista lineas por nota de mayor a menor y escribe el resultado ordenado en notas_ordenadas.txt. Cada línea debe quedar tal cual (con su \n).
lineas_ordenadas = []
# TODO: obtener líneas ordenadas
with open("notas_ordenadas.txt", "w", encoding="utf-8") as f:
f.______ # TODO: escribe la lista ordenada de una vez
Comprobación¶
Se imprime Total de líneas: 4, la segunda y la última sin \n. notas_ordenadas.txt existe con las 4 líneas con el nombre del alumno y su nota ordenadas de mayor a menor nota.
Reflexión¶
¿Qué hemos aprendido en este paso?
Respuestas
readlines()devuelve una lista manipulable con todas las herramientas de Python (sorted, slicing, indexado…).- Cada elemento incluye el
\n writelines()permite escribir una lista de strings de una sola vez.sorted()conkeypermite ordenar por un criterio extraído de cada string de la lista.
Paso 4 — Iteración directa sobre el fichero paso4.py¶
Conceptos
Python permite iterar directamente sobre el objeto fichero: for linea in f:. Es la forma más Pythónica: lee línea a línea sin cargar todo en memoria. Es la opción recomendada para recorrer todas las líneas. Cuando leemos línea a línea deberíamos hacerlo siempre de esta forma, no como se hizo en el paso 2 con while (se usó para ver la función readline()).
Objetivo¶
Iterar sobre el fichero y mostrar cada línea limpia de \n.
Tarea¶
Inserta en paso4.py el siguiente programa:
Pista
for linea in f es equivalente a llamar readline() repetidamente hasta obtener "", pero más conciso y eficiente.
Pregunta:¶
¿En qué se diferencia
for linea in fde usarreadlines()seguido defor linea in lineas?
Respuesta
Con iteración directa, el fichero se lee línea a línea sin cargar todo en memoria. Con readlines(), todas las líneas se cargan primero en una lista. Para ficheros pequeños el resultado es idéntico; para ficheros grandes, la iteración directa es mucho más eficiente.
Ejecuta el script y verifica la salida.
TODO¶
Modifica paso4.py de forma que en una sola pasada sobre el fichero (sin listas intermedias), calcula simultáneamente el número de alumnos, la suma de notas, los aprobados y los suspensos. Imprime el resumen al final.
aprobados = 0
suspensos = 0
suma_notas = 0.0
num_notas = 0
with open("notas.txt", "r", encoding="utf-8") as f:
for linea in f:
print(linea.strip())
# TODO: obtener aprobados, suspensos, suma_notas, num_notas
# Ayúdate de strip().split(": ") para parsear líneas de la forma clave: valor
print(f"Número de alumnos: {num_notas} | Aprobados: {aprobados} | Suspensos: {suspensos}")
print(f"Media: {______:.2f}")
Comprobación¶
Se imprimen las 4 líneas sin \n. El TODO muestra el recuento y la media correctos.
Reflexión¶
¿Qué hemos aprendido en este paso?
Respuestas
- La iteración directa sobre el fichero permite procesar línea a línea sin cargar todo en memoria.
- Es posible acumular varios resultados en una sola pasada usando variables contadoras.
- El patrón
strip().split(": ")es la forma estándar de parsear líneas con formatoclave: valor.
Paso 5 — Añadir notas con modo "a" paso5.py¶
Conceptos
Modo "a" (append): escribe al final del fichero sin borrar lo que había. Si el fichero no existe, lo crea.
Diferencia clave: "w" destruye el contenido previo, "a" lo conserva.
Objetivo¶
Añadir dos alumnos nuevos al fichero sin borrar los cuatro existentes.
Tarea¶
Inserta en paso5.py el siguiente programa:
Tras añadir, verifica el resultado leyendo el fichero completo:
Pista
Ejecuta el script de añadir varias veces y observa cómo crece el fichero. Eso confirma que "a" no borra.
Pregunta:¶
Si ejecutas el script de añadir tres veces seguidas, ¿cuántas líneas tendrá el fichero?
Respuesta
4 + 2×3 = 10 líneas. Cada ejecución añade 2 líneas al final sin borrar las anteriores.
Ejecuta el script una vez (partiendo de notas.txt con 4 líneas) y verifica la salida.
TODO¶
Tras el append, reabre notas.txt en modo "r" y verifica que el número de líneas es exactamente el esperado. Imprime "OK" si es correcto o "ERROR: N líneas encontradas" si no.
LINEAS_ESPERADAS = 6
with open("notas.txt", "r", encoding="utf-8") as f:
lineas = ______
if ______:
print("OK")
else:
print(f"ERROR: {______} líneas encontradas")
Comprobación¶
El fichero tiene 6 líneas tras la primera ejecución; el TODO imprime OK. Si ejecutas el script de nuevo sin restaurar el fichero, el TODO imprimirá ERROR.
Reflexión¶
¿Qué hemos aprendido en este paso?
Respuestas
- El modo
"a"acumula contenido en cada ejecución; es importante verificar el estado real del fichero tras cada operación. - Reabrir en
"r"inmediatamente después de"a"permite comprobar el resultado sin inspeccionar el fichero manualmente. "w"y"a"son de intención opuesta: uno destruye el contenido previo, el otro lo preserva.
Prueba manual guiada¶
- Ejecuta
paso0.pypara restaurarnotas.txta 4 líneas. - Ejecuta
paso5.pyuna vez → verifica que imprimeOK. - Ejecuta
paso5.pyde nuevo → ¿qué imprime ahora? ¿Por qué?
Paso 6 — Escritura con writelines() y print(..., file=f) paso6.py¶
Conceptos
- Como ya hemos visto,
writelines(lista): escribe los elementos de una lista uno tras otro, sin añadir\nautomáticamente. print(valor, file=f): escribe en el fichero igual que en pantalla, y sí añade\nautomáticamente al final.
Objetivo¶
Crear notas_nuevas.txt usando primero writelines() y luego print(..., file=f).
Tarea¶
Parte A — writelines():
Inserta en paso6_a.py el siguiente programa:
Parte B — print(..., file=f):
Inserta en paso6_b.py el siguiente programa:
Pista
En la Parte A, si olvidas los \n dentro de los strings de la lista, todas las líneas quedarán pegadas en una sola.
Pregunta:¶
¿Cuál es la diferencia principal entre
writelines()yprint(..., file=f)respecto al salto de línea?
Respuesta
writelines() no añade \n automáticamente — tienes que incluirlo en los strings de la lista. print(..., file=f) sí añade \n al final de cada llamada, igual que hace en pantalla.
Ejecuta ambas partes y abre notas_nuevas.txt después de ejecutar cada una para verificar que el resultado es idéntico.
TODO¶
Genera paso6_.py de forma que en lugar de la lista de la Parte A, generamos la lista a partir de un diccionario datos_dict. El resultado en notas_nuevas.txt debe ser idéntico.
datos_dict = {
"Elena Torres": 9.0,
"Marco Ruiz": 6.5,
"Sara Vega": 8.2,
}
# TODO: pasa datos en datos_dict a una lista
with open("notas_nuevas.txt", "w", encoding="utf-8") as f:
f.writelines(______) # TODO: usa writelines para escribir la lista obtenida anteiormente
Comprobación¶
notas_nuevas.txt tiene 3 líneas bien separadas en ambos casos y en el TODO.
Reflexión¶
¿Qué hemos aprendido en este paso?
Respuestas
writelines()escribe cada elemento de un iterable sin separadores; el\ndebe estar en los propios strings.print(..., file=f)añade\nautomáticamente al final, igual que en pantalla; más cómodo para formato variable.- Las comprensiones de lista permiten generar la lista para
writelines()de forma concisa a partir de otras estructuras.
Reto de restricciones pasos 5 y 6¶
Una vez completado el paso anterior:
Restricción
Reescribe los scripts finales de los pasos 5 y 6 usando exclusivamente print(..., file=f) para toda escritura.
Paso 7 — Modo "x": creación exclusiva paso7.py¶
Conceptos
Modo "x": crea un fichero nuevo. Si el fichero ya existe, falla con un error. Es la opción más segura cuando no quieres sobrescribir datos existentes por accidente.
Objetivo¶
Observar el comportamiento de "x" al crear un fichero nuevo y al intentar crearlo de nuevo.
Tarea¶
Inserta en paso7.py el siguiente programa:
Primera ejecución — crea el fichero:
Segunda ejecución — ejecuta exactamente el mismo código de nuevo y observa qué ocurre.
Pista
El segundo intento fallará. Lee el mensaje de error en la terminal. ¿Qué tipo de error es? No hace falta gestionarlo con try/except; solo obsérvalo.
Pregunta:¶
¿En qué situación real usarías
"x"en lugar de"w"?
Respuesta
Cuando quieres asegurarte de no sobrescribir un fichero que ya contiene datos importantes. Por ejemplo, al generar ficheros de exportación con nombre único por fecha/hora.
TODO¶
Modifica paso7.py para que pida al usuario el nombre del fichero por teclado, lo cree con modo "x" y escriba en él el nombre y la cantidad de caracteres que tiene ese nombre. Ejecuta dos veces con el mismo nombre y observa qué ocurre.
nombre_fichero = input("Nombre del fichero a crear (sin extensión): ") + ".txt"
with open(______, ______, encoding="utf-8") as f:
f.write(f"Fichero: {______}\n")
f.write(f"Longitud del nombre: {______} caracteres\n")
# TODO: añade también una línea con la fecha y la hora actual
print(f"'{nombre_fichero}' creado correctamente.")
Comprobación¶
- Primera ejecución:
registro.txtse crea y contienePrimer registro. - Segunda ejecución: el programa termina con un error
FileExistsErrorvisible en la terminal. - TODO: el fichero con el nombre introducido se crea y contiene el nombre y su longitud.
Reflexión¶
¿Qué hemos aprendido en este paso?
Respuestas
- El modo
"x"falla conFileExistsErrorsi el fichero ya existe; es el mecanismo de protección sin necesidad de comprobaciones previas. - A diferencia de
"w","x"garantiza que nunca se sobreescribe un fichero existente de forma accidental. - El nombre del fichero puede ser dinámico (variable, entrada de usuario) sin cambiar la lógica de apertura.
Paso 8 — Informe final paso8.py¶
Conceptos
Integramos todo lo aprendido: leer con iteración directa, procesar datos línea a línea y escribir el resultado en un nuevo fichero con print(..., file=f).
Objetivo¶
Leer notas.txt, calcular la media de las notas y generar informe.txt con el listado completo y la media del grupo.
Tarea¶
Inserta en paso8.py el siguiente programa:
Pregunta:¶
¿Qué hace
linea.strip().split(": ")sobre la cadena"Ana García: 8.5\n"?
Respuesta
Primero strip() elimina el \n del final, dejando "Ana García: 8.5". Luego split(": ") divide en la lista ["Ana García", "8.5"].
Ejecuta el script y abre informe.txt para verificar su contenido.
TODO¶
Añade al informe ya generado (usando modo "a" para no borrarlo) tres líneas adicionales al final: el alumno con la nota más alta, el de nota más baja y el total de aprobados.
Comprobación¶
informe.txt Debería quedar de la forma:
=== Informe de notas ===
Ana García: 8.5 — APROBADO
Carlos López: 6.0 — APROBADO
María Fernández: 9.2 — APROBADO
Pedro Martín: 4.5 — SUSPENDIDO
Media del grupo: 7.05
Mejor nota: María Fernández (9.2)
Peor nota: Pedro Martín (4.5)
Aprobados: 4 de 4
Reflexión¶
¿Qué hemos aprendido en este paso?
Respuestas
- Lectura y escritura pueden combinarse en un mismo script usando bloques
withseparados o consecutivos. - El modo
"a"permite añadir datos a un fichero ya existente sin perder su contenido previo.
🔄 Kata de refactor — Paso 8¶
Una vez que el informe funciona, refactoriza el código:
Kata 1
Reescribe la lectura de notas.txt usando readlines() en lugar de iteración directa. ¿Cambia el resultado?
Kata 2
Reescribe la escritura del informe usando write() en lugar de print(..., file=f). ¿Qué tienes que cambiar?
Kata 3 — avanzado
Reescribe el script de forma que lectura y escritura ocurran dentro de un único bloque with, usando la sintaxis de múltiples ficheros:
Comprobaciónlist de entrega¶
Al finalizar el lab, marca lo que has conseguido:
He creado notas.txt con modo "w" y write().
He leído el fichero con al menos tres métodos distintos (read(), readline(), iteración directa).
He añadido líneas con modo "a" sin borrar el contenido previo.
He usado writelines() con los \n correctamente en los elementos.
He usado print(..., file=f) para escribir en un fichero.
He probado el modo "x" y entiendo cuándo usarlo.
He generado el fichero informe.txt con el script integrador.
Activides de ampliación¶
Si has completado todos los pasos, continúa aquí. Cada ampliación se escribe en un fichero propio:
ampliacion_a.py,ampliacion_b.py, etc. (salvo la F, que usagestor.py).
Ampliación A — Encapsular en funciones (tras Paso 0 y Paso 5) ampliacion_a.py¶
Reescribe las operaciones básicas como funciones reutilizables:
Verifica que las tres funciones producen el mismo resultado que el código original de los Pasos 0 y 5.
Ampliación B — Comprensiones de lista (tras Paso 3) ampliacion_b.py¶
A partir de readlines(), obtén en una sola línea de código:
- Lista con solo los nombres (sin notas ni
\n): - Lista con solo las notas como
float: - La media directamente desde esa lista:
Ampliación C — Construir un diccionario (tras Paso 4) ampliacion_c.py¶
Usa la iteración directa para construir un diccionario {nombre: nota}:
Una vez construido, imprime solo los alumnos con nota >= 5 usando una comprensión de diccionario.
Ampliación D — Leer en bloques (tras Paso 2) ampliacion_d.py¶
Usa read(n) en un bucle para leer el fichero en bloques de 8 caracteres:
Responde: ¿cuántos bloques tiene el fichero? ¿Coincide exactamente con len(contenido) / 8? ¿Por qué puede no ser exacto?
Ampliación E — Clase RegistroNotas (tras Paso 8) ampliacion_e.py¶
Encapsula todas las operaciones del lab en una clase:
Prueba con:
Ampliación F — Reto final¶
Sin plantilla. Sin pistas.
Escribe un script gestor.py que muestre un menú en bucle:
Requisitos:
- Los datos se guardan en
alumnos.txtcon modo"a". - La opción 2 lee e imprime con iteración directa.
- La opción 3 escribe
informe.txtconprint(..., file=f). - Sin
try/exceptnipathlib. - Toda la lógica de ficheros en métodos de una clase.