Estructuras de Datos y Proyecto Final

3.07 · Struct y Archivos

PF-307 ⏱ 90 min ⭐ 120 XP intermedio

🧠 Teoría y conceptos — 🧱 1. ¿Qué es un struct?

Struct: agrupación de datos
Struct: agrupación de datos

Figura: Estructura de un struct

Un struct (estructura) es un tipo de dato compuesto que agrupa varios campos relacionados:

#include <string>
using namespace std;

struct Alumno {

string nombre;

int edad;

float promedio;

bool aprobado;

};

Ahora puedes crear variables de tipo Alumno:

Alumno a1;

a1.nombre = "Ana";

a1.edad = 20;

a1.promedio = 8.7;

a1.aprobado = true;

cout << a1.nombre << " tiene promedio " << a1.promedio << endl;

O inicializar directamente:

Alumno a2 = {"Luis", 22, 9.1, true};

📚 2. Arreglos de structs

Archivos: lectura y escritura
Archivos: lectura y escritura

Figura: Array de structs

Alumno grupo[3] = {

{"Ana", 20, 8.7, true},
{"Luis", 22, 9.1, true},
{"Mara", 19, 5.8, false}

};

// Recorrer el arreglo

for (int i = 0; i < 3; i++) {
cout << grupo[i].nombre << " - " << grupo[i].promedio << endl;

}

Ejemplo: encontrar al mejor alumno

Alumno mejor = grupo[0];

for (int i = 1; i < 3; i++) {
if (grupo[i].promedio > mejor.promedio) {

mejor = grupo[i];

}

}

cout << "Mejor: " << mejor.nombre << " con " << mejor.promedio << endl;

Nota: con structs puedes hacer asignación directa (`mejor = grupo[i]`), no necesitas copiar campo por campo. Esto es una gran ventaja sobre los arreglos.

🔧 3. Funciones que reciben structs

Por valor (copia)

void imprimir(Alumno a) {

cout << a.nombre << " - " << a.promedio << endl;

}

Por referencia (sin copia, más eficiente)

void aprobar(Alumno& a) { // '&' = referencia

a.aprobado = true;

}

Por puntero

void imprimirPuntero(Alumno* a) {

cout << a->nombre << " - " << a->promedio << endl;

// Operador flecha '->' en vez de '.'

}

imprimirPuntero(&grupo[0]); // paso dirección con '&'

Regla: usa referencia (`&`) para modificar el original o evitar copias grandes. Usa **puntero (`*`)** cuando la función puede recibir `nullptr` (puntero nulo).

🆚 4. Struct vs Class (preview)

Regla práctica: usa struct cuando solo quieres agrupar datos. Usa class cuando además hay comportamiento (métodos, encapsulación). En C++ la diferencia es casi solo filosófica; en otros lenguajes (Java, C#) los structs tienen semánticas más restrictivas.

📁 5. Archivos: el problema de la volatilidad

Memoria RAM (donde viven tus variables) se borra al cerrar el programa. Disco duro (archivos) persiste entre ejecuciones.

#include <fstream>  // para archivos
using namespace std;

Las 3 clases principales

✍️ 6. Escribir en un archivo

#include <fstream>
#include <iostream>
using namespace std;
int main() {

ofstream archivo("datos.txt"); // abre para escribir

if (!archivo.is_open()) {
cout << "No se pudo abrir el archivo" << endl;
return 1;

}

archivo << "Hola mundo desde un archivo" << endl;

archivo << 42 << endl;

archivo << 3.14 << endl;

archivo.close(); // siempre cerrar

return 0;

}

Resultado (en datos.txt):

Hola mundo desde un archivo

42

3.14

Modos de apertura

Combinar: fstream archivo("datos.txt", ios::in | ios::out);

Append: agregar al final sin sobrescribir

ofstream archivo("datos.txt", ios::app);

archivo << "Nueva línea al final" << endl;

archivo.close();

📖 7. Leer de un archivo

#include <fstream>
#include <iostream>
#include <string>
using namespace std;
int main() {

ifstream archivo("datos.txt");

if (!archivo.is_open()) {
cout << "No se pudo abrir" << endl;
return 1;

}

string linea;

while (getline(archivo, linea)) {  // lee línea por línea
cout << linea << endl;

}

archivo.close();

return 0;

}

Leer números

ifstream archivo("numeros.txt");

int n;

while (archivo >> n) {  // lee hasta que falle (fin de archivo)
cout << "Leí: " << n << endl;

}

archivo.close();

Truco: el operador `>>` aplicado a un `ifstream` retorna `false` cuando llega al fin del archivo. Es la forma idiomática de leer "todo".

🔁 8. Guardar y leer un arreglo de structs (CSV simple)

#include <fstream>
#include <string>
using namespace std;

struct Alumno {

string nombre;

int edad;

float promedio;

};

// Guardar 3 alumnos en CSV

void guardar(Alumno grupo[], int n) {

ofstream archivo("alumnos.csv");

for (int i = 0; i < n; i++) {

archivo << grupo[i].nombre << ","

<< grupo[i].edad << ","

<< grupo[i].promedio << endl;

}

archivo.close();

}

// Leer

int cargar(Alumno grupo[], int maxN) {

ifstream archivo("alumnos.csv");

int i = 0;

string linea;

while (getline(archivo, linea) && i < maxN) {

size_t p1 = linea.find(',');

size_t p2 = linea.find(',', p1 + 1);

grupo[i].nombre = linea.substr(0, p1);

grupo[i].edad = stoi(linea.substr(p1 + 1, p2 - p1 - 1));

grupo[i].promedio = stof(linea.substr(p2 + 1));

i++;

}

archivo.close();

return i;  // cuántos se cargaron

}

Salida de `alumnos.csv`:

Ana,20,8.7

Luis,22,9.1

Mara,19,5.8

🛡️ 9. Validación al abrir archivos

Siempre verifica que el archivo se abrió correctamente:

ifstream archivo("datos.txt");

if (!archivo) {  // o !archivo.is_open()

cerr << "Error: no se pudo abrir datos.txt" << endl;

cerr << "Verifica que el archivo existe y tienes permisos" << endl;

return 1;  // código de error

}

Regla de oro: un programa que abre archivos sin validar es un programa que crashea en producción.

🧪 13. LAB 3.7 — Agenda de Contactos Persistente (4 h)

Descripción

Crea una agenda que guarde contactos en un archivo. Cada contacto tiene: nombre, teléfono, email, categoria (familia, amigo, trabajo).

Funcionalidades:

  1. Agregar contacto (lo guarda en el archivo).
  2. Listar todos (lee del archivo y los muestra).
  3. Buscar por nombre (búsqueda lineal o binaria si están ordenados).
  4. Eliminar contacto (reescribe el archivo sin él).
  5. Estadísticas: cuántos contactos hay por categoría.

Rúbrica de evaluación

Bonus XP

  • +30 XP si la búsqueda es case-insensitive (ignora mayúsculas/minúsculas).
  • +20 XP si ordenas los contactos por nombre al listarlos.
  • +20 XP si exportas los contactos a JSON (formato más estructurado).
  • +10 XP si el programa maneja archivos binarios como opción alternativa.

🔚 15. Cierre — Cuaderno del programador

  1. Diseña tu propio `struct` para un dominio que te interese (videojuegos, deportes, música). Lista 5 campos.
  2. Imagina que tu struct se guarda en un archivo. Dibuja el formato de cada línea (CSV, JSON, binario).
  3. Reflexión: antes de este tema, ¿dónde se "guardaban" tus variables? ¿Por qué no era suficiente?
  4. Compromiso: la próxima vez que escribas un programa, pregúntate si los datos deben persistir. Si sí, usa archivos desde el inicio.

🏅 Insignia y XP del tema

🔗 ¿Qué sigue?

Tema 3.8 — Debugging Profesional y Git Avanzado. Aprende a usar un debugger de verdad (puntos de interrupción, inspección de variables, step-over) y a trabajar con ramas en Git (merge, rebase, resolución de conflictos). Son las dos herramientas que separan a un junior de alguien que puede trabajar en equipo.

Antes de avanzar:

  • [ ] LAB 3.7 entregado y funcionando
  • [ ] Quest mixto respondido en el cuaderno
  • [ ] Reflexión escrita

*"Un programa sin persistencia es un programa que nunca existió."*

⚠️ Errores típicos — ⚠️ 12. Errores típicos del razonamiento

Error 1: No cerrar el archivo

ofstream archivo("datos.txt");

archivo << "algo";

// el archivo se cierra al salir de la función, pero...

// si crashea antes, los datos se pierden

Solución: cerrar explícitamente o usar bloques {} (RAII).

Error 2: Confundir ofstream con ifstream

ofstream archivo("datos.txt"); // ESCRITURA

archivo >> x; // MAL: no puedes leer de un ofstream

Error 3: Olvidar el ios::app para no sobrescribir

ofstream archivo("log.txt"); // sobrescribe

ofstream archivo("log.txt", ios::app); // agrega al final

Error 4: Asumir que el archivo existe al leer

ifstream archivo("datos.txt");

string linea;

getline(archivo, linea); // No lee nada, archivo no existe

cout << linea;  // imprime vacío

Siempre verifica archivo.is_open().

Error 5: Comparar structs con ==

Alumno a = {"Ana", 20, 8.7};
Alumno b = {"Ana", 20, 8.7};
if (a == b) ...  // MAL: el operador == no está definido para Alumno

Solución: compara campo por campo:

if (a.nombre == b.nombre && a.edad == b.edad && a.promedio == b.promedio) ...

O define el operador (lo verás en temas avanzados).

🤖 IA como copiloto — 🤖 11. AI Mission — "Dame el formato, no la lógica"

Objetivo: usar la IA para detalles de formato (CSV, JSON), no para la lógica de persistencia.

Escenario: necesitas guardar 100 alumnos en un archivo, pero no sabes qué formato usar (CSV, JSON, binario).

Prompt sugerido:

"Quiero guardar una lista de 100 alumnos (nombre, edad, promedio) en C++ para que el programa pueda recargarla al inicio. Compárame 3 formatos (CSV, JSON, binario) y dime las ventajas/desventajas de cada uno. Dame un ejemplo de código para leer/escribir en cada formato."

Lo que NO debes hacer:

  • Pedirle el LAB completo.
  • Aceptar el primer formato sin pensarlo.

Lo que SÍ debes hacer:

  • Decidir qué formato usar según el caso (CSV si quieres que un humano lo abra en Excel, binario si quieres velocidad).
  • Implementar la lectura/escritura a mano después.

Ritual de 3 min: si la IA te da 3 formatos y no sabes cuál elegir, pregúntate: ¿quién va a leer este archivo? ¿Humano o máquina? Esa respuesta decide.

🧪 Laboratorio — 🔒 10. Buenas prácticas con archivos

1. Siempre cerrar

archivo.close();

// o usar RAII (preferido)

{

ofstream archivo("datos.txt");

archivo << "...";

} // se cierra automáticamente al salir del bloque

2. Usar '\n' en vez de endl para rendimiento

archivo << "línea\n"; // más rápido

archivo << "línea" << endl; // también descarga el buffer

En archivos con millones de líneas, '\n' es 10x más rápido.

3. No dejar archivos abiertos

Un archivo abierto en Windows puede bloquear el borrado o renombre. Cierra siempre.

4. Usar rutas absolutas en producción

ofstream archivo("/var/data/log.txt"); // Linux

ofstream archivo("C:\\datos\\log.txt"); // Windows

Las rutas relativas dependen del directorio de ejecución, que puede cambiar.

📝 Quest · Cuestionario — 📝 Quest — Reactivos para autoevaluación

Las siguientes preguntas te sirven para autoevaluarte después de leer el tema. Responde en tu cuaderno o mentalmente, y luego revisa las Respuestas modelo (disponibles en libre acceso, sin iniciar sesión).

Sección A · Opción múltiple

Elige la opción correcta (A, B, C o D). Las respuestas están en la sección Respuestas modelo.

  1. ¿Qué es un struct en C++?
    • Una función
    • Un tipo de dato que agrupa variables
    • Un bucle
    • Una clase idéntica
  2. ¿Cómo accedes a un campo de un struct?
    • struct->campo
    • struct.campo o p->campo si es puntero
    • struct[campo]
    • struct::campo
  3. ¿Cómo defines un struct en C++?
    • type Persona { }
    • struct Persona { string nombre; int edad; };
    • Persona struct { }
    • Persona { }
  4. ¿Qué hace std::ofstream?
    • Lee archivos
    • Escribe/crea archivos
    • Compila
    • Borra archivos
  5. ¿Qué hace std::ifstream?
    • Escribe
    • Lee archivos
    • Borra
    • Crea
  6. ¿Cómo abres un archivo para escribir en C++?
    • fopen("a.txt", "r")
    • std::ofstream f("a.txt");
    • file f("a.txt");
    • open("a.txt");
  7. ¿Cómo verificas que un archivo se abrió bien?
    • f.good()
    • f.is_open()
    • f.ok()
    • f.alive()
  8. ¿Cómo leer línea por línea en C++?
    • f.readLine()
    • std::getline(f, linea)
    • f.getline(linea)
    • read(f, linea)
  9. Para leer un struct desde un archivo:
    • readFile
    • f.read(reinterpret_cast<char*>(&s), sizeof(s));
    • Solo strings
    • No se puede
  10. ¿Por qué usar archivos en programas?
    • Para hacer lento
    • Para persistir datos entre ejecuciones
    • Para compilar
    • Para debug

Sección B · Preguntas abiertas

Desarrolla tu respuesta en al menos 3 líneas. Compara con la respuesta modelo después de escribir.

  1. Diseña un struct "Estudiante" con nombre, edad, promedio. Escribe un programa que lo lea de un archivo y muestre el mejor promedio.
    → Escribe tu respuesta aquí (en tu cuaderno o mentalmente)…
  2. Explica la diferencia entre fopen (C) y ifstream (C++). ¿Cuándo usar cada uno?
    → Escribe tu respuesta aquí (en tu cuaderno o mentalmente)…
  3. ¿Qué ventajas tiene leer/escribir structs completos vs campo a campo?
    → Escribe tu respuesta aquí (en tu cuaderno o mentalmente)…

🟢 Cuando termines, revisa las Respuestas modelo y compáralas con las tuyas. La mejor forma de aprender es discutir cada respuesta contigo mismo o con un compañero.

← Volver a temas del capítulo 📚 Ver todos los temas 🔑 Inicia sesión para hacer el Test