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

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

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:
- Agregar contacto (lo guarda en el archivo).
- Listar todos (lee del archivo y los muestra).
- Buscar por nombre (búsqueda lineal o binaria si están ordenados).
- Eliminar contacto (reescribe el archivo sin él).
- 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
- Diseña tu propio `struct` para un dominio que te interese (videojuegos, deportes, música). Lista 5 campos.
- Imagina que tu struct se guarda en un archivo. Dibuja el formato de cada línea (CSV, JSON, binario).
- Reflexión: antes de este tema, ¿dónde se "guardaban" tus variables? ¿Por qué no era suficiente?
- 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.
- ¿Qué es un struct en C++?
- Una función
- Un tipo de dato que agrupa variables
- Un bucle
- Una clase idéntica
- ¿Cómo accedes a un campo de un struct?
- struct->campo
- struct.campo o p->campo si es puntero
- struct[campo]
- struct::campo
- ¿Cómo defines un struct en C++?
- type Persona { }
- struct Persona { string nombre; int edad; };
- Persona struct { }
- Persona { }
- ¿Qué hace std::ofstream?
- Lee archivos
- Escribe/crea archivos
- Compila
- Borra archivos
- ¿Qué hace std::ifstream?
- Escribe
- Lee archivos
- Borra
- Crea
- ¿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");
- ¿Cómo verificas que un archivo se abrió bien?
- f.good()
- f.is_open()
- f.ok()
- f.alive()
- ¿Cómo leer línea por línea en C++?
- f.readLine()
- std::getline(f, linea)
- f.getline(linea)
- read(f, linea)
- Para leer un struct desde un archivo:
- readFile
- f.read(reinterpret_cast<char*>(&s), sizeof(s));
- Solo strings
- No se puede
- ¿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.
- 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)…
- Explica la diferencia entre fopen (C) y ifstream (C++). ¿Cuándo usar cada uno?→ Escribe tu respuesta aquí (en tu cuaderno o mentalmente)…
- ¿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.