Estructuras de Datos y Proyecto Final

3.03 · Cadenas de Caracteres y Expresiones Regulares

PF-303 ⏱ 90 min ⭐ 100 XP intermedio

🧠 Teoría y conceptos — 🔤 1. Strings en C: la historia que duele

Cadenas de caracteres y expresiones regulares
Cadenas de caracteres y expresiones regulares

En C (y C++ viejo), una cadena es un arreglo de `char` terminado en `\0`.

Figura: String como arreglo de caracteres

char saludo[6] = {'H', 'o', 'l', 'a', '!', '\0'};

// o más corto:

char saludo[] = "Hola!";

El compilador cuenta los caracteres y agrega el `\0` automáticamente. Por eso "Hola!" ocupa 6, no 5.

El carácter \0 es el centinela

\0 (carácter nulo, ASCII 0) marca el final de la cadena. Todas las funciones de C lo buscan para saber dónde parar:

char palabra[] = "Perro";

cout << palabra << endl;        // imprime hasta el \0
cout << palabra[3] << endl;     // 'r'
cout << palabra[5] << endl;     // '\0' (no se ve, pero está)

⚠️ Trampa mortal: olvidar el \0

char nombre[5] = {'A', 'd', 'a', 'n', '\0'};  // BIEN
char malo[4]  = {'A', 'd', 'a', 'n'};          // MAL: no termina en \0
cout << malo;  // imprime "Adan" + basura de memoria hasta encontrar un \0

💪 2. std::string: el salvavidas de C++

C++ moderno usa std::string que maneja la memoria por ti:

#include <string>
using namespace std;

string nombre = "Ana";

cout << nombre.length() << endl;    // 3 (no 4: NO cuenta el \0)
cout << nombre.size() << endl;      // también 3
cout << nombre.empty() << endl;     // false (0)

Comparación con Java y Python

Nota mental: en C++ == compara contenido (no referencia como en C con punteros). En Java == compara referencia (casi nunca lo que quieres); usa .equals().

🛠️ 3. Métodos esenciales de std::string

Concatenar y asignar

string saludo = "Hola";

string nombre = "Mundo";

string mensaje = saludo + ", " + nombre + "!";

cout << mensaje << endl;  // "Hola, Mundo!"

saludo += " a todos"; // suma en sitio

cout << saludo << endl; // "Hola a todos"

Acceder a caracteres

string palabra = "Programar";

cout << palabra[0] << endl;       // 'P' (sin chequeo de límites)
cout << palabra.at(2) << endl;    // 'o' (lanza excepción si fuera de rango)
cout << palabra.front() << endl;  // 'P' (C++11)
cout << palabra.back() << endl;   // 'r'

Comparar

string a = "abc", b = "abd";

if (a == b) cout << "iguales";
if (a < b)  cout << "a es menor";  // orden lexicográfico

Buscar subcadenas

string texto = "El rápido zorro marrón";

size_t pos = texto.find("zorro");

if (pos != string::npos) {  // npos = "no encontrado"
cout << "Encontrado en posicion " << pos << endl;  // 11

}

Extraer subcadenas

string frase = "Hola Mundo";

string mundo = frase.substr(5, 5); // desde 5, 5 caracteres

cout << mundo << endl;  // "Mundo"

Reemplazar

string s = "Me gusta Java";

s.replace(9, 4, "C++"); // posición 9, longitud 4, reemplazo

cout << s << endl;  // "Me gusta C++"

Limpiar espacios

string conEspacios = " hola ";

conEspacios.erase(0, conEspacios.find_first_not_of(" \t"));

conEspacios.erase(conEspacios.find_last_not_of(" \t") + 1);

cout << "[" << conEspacios << "]" << endl;  // "[hola]"

En la práctica usarás librerías externas (boost::trim), pero el patrón es el mismo.

🔢 4. Conversión entre string y números

#include <string>

// string a número

string s1 = "42";

int n = stoi(s1); // 42

double d = stod("3.14"); // 3.14

// número a string

int edad = 25;

string texto = "Tengo " + to_string(edad) + " años";

cout << texto << endl;  // "Tengo 25 años"

Truco profesional: para leer números de forma segura:

string entrada;

cout << "Dame un numero: ";
cin >> entrada;

try {

int n = stoi(entrada);

cout << "Leíste: " << n << endl;
} catch (const invalid_argument& e) {
cout << "Eso no es un número válido" << endl;

}

🔒 5. Validación de entrada: el pan de cada día

Problema real: leer un número entre 1 y 10, pero el usuario puede teclear "abc" o "-5".

int leerNumeroValido() {

int n;

while (true) {
cout << "Dame un numero entre 1 y 10: ";

string entrada;

cin >> entrada;

try {

n = stoi(entrada);

if (n >= 1 && n <= 10) return n;
cout << "Fuera de rango. ";
} catch (...) {
cout << "No es un numero. ";

}

}

}

Patrón clave: leer como string primero, intentar convertir, validar rango. Nunca leas directo a int y reces por el error.

🧬 6. Expresiones Regulares ()

Una regex es un patrón que describe un conjunto de cadenas.

#include <regex>

string email = "ana@ipn.mx";

regex patron(R"([a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,})");
if (regex_match(email, patron)) {
cout << "Email válido" << endl;
} else {
cout << "Email inválido" << endl;

}

Sintaxis básica

Ejemplo: validar teléfono mexicano (10 dígitos)

string tel = "5512345678";

regex patronTel(R"(^\d{10}$)");
if (regex_match(tel, patronTel)) {
cout << "Teléfono válido" << endl;
} else {
cout << "Debe tener 10 dígitos exactos" << endl;

}

Extraer partes: regex_search con captures

string texto = "Mi email es juan@ipn.mx y mi tel es 5512345678";

regex patron(R"((\w+)@(\w+)\.(\w{2,}))");

smatch matches;

if (regex_search(texto, matches)) {
cout << "Email completo: " << matches[0] << endl;  // juan@ipn.mx
cout << "Usuario: "       << matches[1] << endl;  // juan
cout << "Dominio: "       << matches[2] << endl;  // ipn
cout << "TLD: "           << matches[3] << endl;  // mx

}

🧪 10. LAB 3.3 — Validador de Formularios (4 h)

Descripción

Crea un programa que pida al usuario:

  1. Nombre (solo letras y espacios, 2-50 caracteres)
  2. Email (formato estándar)
  3. Teléfono (10 dígitos, puede tener espacios/guiones que se ignoran)
  4. Edad (número entre 18 y 99)
  5. CURP (formato: 4 letras + 6 dígitos + 6 alfanuméricos + 1 dígito)

El programa debe validar cada campo y permitir reescribirlo si es inválido.

Rúbrica de evaluación

Bonus XP

  • +40 XP si usas funciones separadas para cada validación (validarEmail(), etc.).
  • +30 XP si limpias la entrada antes de validar (trim de espacios).
  • +20 XP si agregas un campo contraseña con regla: mínimo 8 caracteres, al menos una mayúscula, un dígito y un carácter especial.
  • +10 XP si al final muestras un resumen del registro formateado.

🔚 12. Cierre — Cuaderno del programador

  1. Dibuja una tabla comparando C++/Java/Python para: longitud, concatenar, buscar, subcadena.
  2. Escribe 3 regex que entiendas completamente (con palabras, no símbolos).
  3. Reflexión: ¿en qué situación real validarías un email con regex? ¿Cuándo NO usarías regex y mejor buscarías otra librería?
  4. Compromiso: la próxima vez que copies un patrón de Stack Overflow, explica cada símbolo en tu cuaderno antes de usarlo.

🏅 Insignia y XP del tema

🔗 ¿Qué sigue?

Tema 3.4 — Arreglos Bidimensionales (Matrices). Pasamos de cajas lineales a cuadrículas: calificaciones, tableros de juego, imágenes en escala de grises. Verás cómo la memoria es lineal pero el acceso es 2D — un concepto que se vuelve crítico en bases de datos y machine learning.

Antes de avanzar:

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

*"Las cadenas son el 70% del software real. Si las dominas, dominas el texto."*

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

Error 1: Comparar strings con == de Java en C++

// Java

String a = "Hola", b = "Hola";

if (a == b) ...  // MAL: compara referencias, no contenido

// C++

string a = "Hola", b = "Hola";

if (a == b) ...  // BIEN: std::string sobrecarga == para contenido

Error 2: Olvidar que .find() retorna npos

size_t pos = texto.find("xyz");

cout << texto.substr(pos, 3);  // CRASH si pos == npos

Siempre chequea:

if (pos != string::npos) { /* usar pos */ }

Error 3: Modificar un char[] sin recalcular tamaño

char nombre[20] = "Ana";

strcat(nombre, " López"); // puede desbordar si no hay espacio

Con std::string no pasa, pero con char[] necesitas reservar espacio de antemano.

Error 4: Confundir longitud de arreglo con longitud de cadena

char palabra[] = "Hola";

cout << sizeof(palabra) << endl;  // 5 (incluye \0)
cout << strlen(palabra) << endl;  // 4 (sin \0)

sizeof = tamaño en bytes. strlen = caracteres hasta el \0.

Error 5: Regex demasiado complicada

^(([A-Z][a-z]+)\s){2,}[A-Z][a-z]+$
Si no puedes explicar tu regex con palabras simples, está mal. Descomponer siempre.

🤖 IA como copiloto — 🤖 8. AI Mission — "Genera el patrón, no la lógica"

Objetivo: usar la IA para lo que SÍ sirve, no para lo que te quita el aprendizaje.

Escenario: necesitas validar CURP, RFC o placas de auto. El patrón es aburrido y propenso a errores.

Prompt sugerido:

"Dame una expresión regular en C++ que valide una CURP mexicana (formato: 4 letras, 6 dígitos, 6 caracteres alfanuméricos, 1 dígito). No me expliques la lógica del regex paso a paso, solo dame el patrón y un ejemplo de uso con `regex_match`."

Lo que NO debes hacer:

  • Pedirle que te genere el LAB.
  • Copiar la regex sin entender qué hace (prueba con varios casos y observa qué pasa).

Lo que SÍ debes hacer:

  • Tomar el patrón, descomponerlo (AAAA = 4 letras, 990101 = fecha, etc.) y escribirlo en tu cuaderno.
  • Probar con 5 CURPs diferentes (válidas e inválidas) y ver dónde la regex falla.

Ritual de 3 min: si la regex no funciona en tu primer caso de prueba → lee tu cuaderno, no sigas pegándole.

📝 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. En C++, ¿cómo se incluye el header de strings?
    • #include <string>
    • #include "string.h"
    • #include <text>
    • #include <cstring>
  2. ¿Cómo se declara un string en C++?
    • string s;
    • std::string s;
    • char s;
    • A y B son válidas
  3. ¿Qué hace s.length() en C++?
    • Cuenta bytes
    • Devuelve el número de caracteres
    • Convierte a mayúsculas
    • Busca
  4. ¿Cómo concatenar strings en C++?
    • s1 + s2
    • s1.append(s2)
    • A y B
    • concat(s1, s2)
  5. ¿Qué hace s.substr(2, 5)?
    • Reemplaza
    • Devuelve substring desde pos 2 con 5 caracteres
    • Concatena
    • Convierte a mayúsculas
  6. ¿Cómo comparar dos strings en C++?
    • s1 == s2
    • s1.compare(s2)
    • A y B
    • Solo compare()
  7. ¿Qué es un caracter de escape?
    • Un caracter normal
    • Secuencia especial como \n, \t, \
    • Un emoji
    • Una variable
  8. ¿Cómo leer un string con espacios desde consola?
    • cin >> s
    • getline(cin, s)
    • cin.get()
    • scanf
  9. ¿Qué es un string vacío?
    • s = null
    • s = "" o s = string()
    • s = 0
    • s = "0"
  10. ¿Cómo convertir string a número en C++?
    • atoi, stoi
    • parseInt
    • Number(s)
    • int(s)

Sección B · Preguntas abiertas

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

  1. ¿Cuál es la diferencia entre string y char[] en C++? ¿Cuándo usar cada uno?
    → Escribe tu respuesta aquí (en tu cuaderno o mentalmente)…
  2. Explica cómo funciona getline vs cin para leer strings con espacios.
    → Escribe tu respuesta aquí (en tu cuaderno o mentalmente)…
  3. Diseña una función que cuente cuántas palabras tiene un string.
    → 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