3.03 · Cadenas de Caracteres y Expresiones Regulares
🧠 Teoría y conceptos — 🔤 1. Strings en C: la historia que duele

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:
- Nombre (solo letras y espacios, 2-50 caracteres)
- Email (formato estándar)
- Teléfono (10 dígitos, puede tener espacios/guiones que se ignoran)
- Edad (número entre 18 y 99)
- 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
- Dibuja una tabla comparando C++/Java/Python para: longitud, concatenar, buscar, subcadena.
- Escribe 3 regex que entiendas completamente (con palabras, no símbolos).
- 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?
- 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.
- En C++, ¿cómo se incluye el header de strings?
- #include <string>
- #include "string.h"
- #include <text>
- #include <cstring>
- ¿Cómo se declara un string en C++?
- string s;
- std::string s;
- char s;
- A y B son válidas
- ¿Qué hace s.length() en C++?
- Cuenta bytes
- Devuelve el número de caracteres
- Convierte a mayúsculas
- Busca
- ¿Cómo concatenar strings en C++?
- s1 + s2
- s1.append(s2)
- A y B
- concat(s1, s2)
- ¿Qué hace s.substr(2, 5)?
- Reemplaza
- Devuelve substring desde pos 2 con 5 caracteres
- Concatena
- Convierte a mayúsculas
- ¿Cómo comparar dos strings en C++?
- s1 == s2
- s1.compare(s2)
- A y B
- Solo compare()
- ¿Qué es un caracter de escape?
- Un caracter normal
- Secuencia especial como \n, \t, \
- Un emoji
- Una variable
- ¿Cómo leer un string con espacios desde consola?
- cin >> s
- getline(cin, s)
- cin.get()
- scanf
- ¿Qué es un string vacío?
- s = null
- s = "" o s = string()
- s = 0
- s = "0"
- ¿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.
- ¿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)…
- Explica cómo funciona getline vs cin para leer strings con espacios.→ Escribe tu respuesta aquí (en tu cuaderno o mentalmente)…
- 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.