Operaciones avanzadas con cadenas
Las cadenas de texto son uno de los tipos de datos más utilizados en cualquier aplicación, desde la presentación de información al usuario hasta el procesamiento de datos de entrada. C# proporciona un conjunto robusto de herramientas para manipular cadenas de manera eficiente y elegante. En este artículo, exploraremos las operaciones avanzadas que nos permitirán trabajar con texto de forma profesional, incluyendo métodos de manipulación, formateo, búsqueda y análisis de patrones.
Dominar estas técnicas es fundamental para cualquier programador de C#, ya que las cadenas aparecen en prácticamente todos los aspectos del desarrollo de software: interfaces de usuario, procesamiento de archivos, comunicación con APIs, validación de datos y mucho más. Las operaciones que aprenderemos aquí nos proporcionarán las herramientas necesarias para manejar texto con precisión y eficiencia.
Formateo avanzado de cadenas
Interpolación de cadenas
La interpolación de cadenas, introducida en C# 6.0, proporciona una sintaxis limpia y legible para incrustar expresiones dentro de literales de cadena.
string nombre = "Carlos";
int edad = 25;
decimal salario = 3500.75m;
// Interpolación básica
string mensaje = $"Hola, soy {nombre} y tengo {edad} años";
// Con expresiones
string calculado = $"El próximo año tendré {edad + 1} años";
// Con formato
string formateado = $"Mi salario es {salario:C2}";
Console.WriteLine(mensaje);
Console.WriteLine(calculado);
Console.WriteLine(formateado);
Especificadores de formato
Especificador | Descripción | Ejemplo |
---|---|---|
C o c | Moneda | {valor:C} → "3.500,75 €" |
D o d | Decimal | {numero:D5} → "00123" |
F o f | Punto fijo | {decimal:F2} → "123,45" |
N o n | Numérico | {numero:N} → "1.234,56" |
P o p | Porcentaje | {decimal:P} → "12,34 %" |
X o x | Hexadecimal | {numero:X} → "FF" |
double valor = 1234.5678;
DateTime fecha = DateTime.Now;
// Diferentes formatos numéricos
Console.WriteLine($"Moneda: {valor:C}");
Console.WriteLine($"Punto fijo: {valor:F2}");
Console.WriteLine($"Científico: {valor:E2}");
// Formatos de fecha
Console.WriteLine($"Fecha corta: {fecha:d}");
Console.WriteLine($"Fecha larga: {fecha:D}");
Console.WriteLine($"Personalizada: {fecha:dd/MM/yyyy HH:mm}");
Métodos avanzados de manipulación
Métodos de búsqueda y análisis
string texto = "El rápido zorro marrón salta sobre el perro perezoso";
// Búsqueda de posiciones
int posicion = texto.IndexOf("zorro");
int ultimaPosicion = texto.LastIndexOf("el");
bool contiene = texto.Contains("rápido");
Console.WriteLine($"'zorro' está en la posición: {posicion}");
Console.WriteLine($"Última ocurrencia de 'el': {ultimaPosicion}");
Console.WriteLine($"¿Contiene 'rápido'?: {contiene}");
// Búsqueda con opciones de comparación
bool encuentraSinCase = texto.IndexOf("ZORRO", StringComparison.OrdinalIgnoreCase) >= 0;
Console.WriteLine($"Búsqueda sin distinguir mayúsculas: {encuentraSinCase}");
Análisis de contenido
string datos = "123,456.78";
string email = "usuario@dominio.com";
string espacios = " texto con espacios ";
// Análisis de contenido
bool esNumerico = datos.All(char.IsDigit);
bool tieneDigitos = datos.Any(char.IsDigit);
bool soloLetras = email.Take(email.IndexOf('@')).All(char.IsLetter);
Console.WriteLine($"¿Solo dígitos?: {esNumerico}");
Console.WriteLine($"¿Tiene dígitos?: {tieneDigitos}");
Console.WriteLine($"Usuario solo letras: {soloLetras}");
// Limpieza de espacios
string limpio = espacios.Trim();
string sinEspaciosIzq = espacios.TrimStart();
string sinEspaciosDer = espacios.TrimEnd();
Console.WriteLine($"Original: '{espacios}'");
Console.WriteLine($"Limpio: '{limpio}'");
StringBuilder para construcción eficiente
Cuando necesitamos realizar múltiples operaciones de concatenación o modificación de cadenas, StringBuilder
ofrece un rendimiento significativamente mejor que las operaciones directas sobre string
.
using System.Text;
// Construcción eficiente de cadenas largas
StringBuilder constructor = new StringBuilder();
constructor.AppendLine("=== REPORTE DIARIO ===");
constructor.AppendLine($"Fecha: {DateTime.Now:dd/MM/yyyy}");
constructor.AppendLine();
// Agregar contenido dinámicamente
for (int i = 1; i <= 5; i++)
{
constructor.AppendLine($"Elemento {i}: Procesado correctamente");
}
constructor.AppendLine();
constructor.Append("Total de elementos procesados: ");
constructor.Append(5);
string reporte = constructor.ToString();
Console.WriteLine(reporte);
Métodos útiles de StringBuilder
StringBuilder sb = new StringBuilder("Texto inicial");
// Inserción en posiciones específicas
sb.Insert(0, ">>> ");
sb.Insert(sb.Length, " <<<");
// Reemplazo de contenido
sb.Replace("inicial", "modificado");
// Eliminación de caracteres
sb.Remove(0, 4); // Elimina los primeros 4 caracteres
// Control de capacidad para optimización
StringBuilder optimizado = new StringBuilder(1000); // Capacidad inicial
Console.WriteLine($"Capacidad actual: {optimizado.Capacity}");
Console.WriteLine($"Resultado final: {sb.ToString()}");
Expresiones regulares básicas
Las expresiones regulares proporcionan una herramienta poderosa para el análisis y manipulación de patrones en cadenas de texto.
using System.Text.RegularExpressions;
string textoConEmails = "Contactos: juan@empresa.com, maria@ejemplo.es, carlos@test.org";
// Patrón para emails
string patronEmail = @"\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Z|a-z]{2,}\b";
Regex regexEmail = new Regex(patronEmail);
// Encontrar todas las coincidencias
MatchCollection emails = regexEmail.Matches(textoConEmails);
Console.WriteLine($"Se encontraron {emails.Count} emails:");
foreach (Match email in emails)
{
Console.WriteLine($"- {email.Value}");
}
Patrones comunes
Patrón | Descripción | Expresión regular |
---|---|---|
Dirección de correo | \b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Z|a-z]{2,}\b |
|
Teléfono | Número telefónico español | ^[6-9]\d{8}$ |
DNI | Documento nacional | ^\d{8}[A-HJ-NP-TV-Z]$ |
Código postal | CP español | ^[0-5]\d{4}$ |
// Validación con expresiones regulares
string[] telefonos = { "612345678", "912345678", "512345678", "abc123456" };
Regex patronTelefono = new Regex(@"^[6-9]\d{8}$");
Console.WriteLine("Validación de teléfonos:");
foreach (string telefono in telefonos)
{
bool esValido = patronTelefono.IsMatch(telefono);
Console.WriteLine($"{telefono}: {(esValido ? "Válido" : "Inválido")}");
}
Manipulación avanzada y transformaciones
Operaciones de caso y formato
string textoMixto = "EsTE es Un TeXTO con ForMATO mixTO";
// Transformaciones de caso
string mayusculas = textoMixto.ToUpper();
string minusculas = textoMixto.ToLower();
string capitalizado = System.Globalization.CultureInfo.CurrentCulture.TextInfo
.ToTitleCase(textoMixto.ToLower());
Console.WriteLine($"Original: {textoMixto}");
Console.WriteLine($"Mayúsculas: {mayusculas}");
Console.WriteLine($"Minúsculas: {minusculas}");
Console.WriteLine($"Capitalizado: {capitalizado}");
// Normalización para comparaciones
string texto1 = "café";
string texto2 = "CAFÉ";
bool sonIguales = string.Equals(texto1, texto2, StringComparison.OrdinalIgnoreCase);
Console.WriteLine($"¿'{texto1}' y '{texto2}' son iguales?: {sonIguales}");
División y unión avanzada
string csvData = "nombre,edad,ciudad,salario";
string textoMultilinea = @"Primera línea
Segunda línea con espacios extra
Tercera línea
Línea vacía siguiente
Quinta línea";
// División con múltiples separadores
string[] columnas = csvData.Split(',');
string[] lineas = textoMultilinea.Split(new[] { '\r', '\n' },
StringSplitOptions.RemoveEmptyEntries);
// Unión con separadores personalizados
string resultado = string.Join(" | ", columnas);
Console.WriteLine($"Columnas unidas: {resultado}");
// Procesamiento de líneas
Console.WriteLine("Líneas procesadas:");
foreach (string linea in lineas)
{
string lineaLimpia = linea.Trim();
if (!string.IsNullOrWhiteSpace(lineaLimpia))
{
Console.WriteLine($"- '{lineaLimpia}'");
}
}
Codificación y conversiones
Trabajo con diferentes codificaciones
using System.Text;
string textoConAcentos = "Niño, corazón, programación";
// Diferentes codificaciones
byte[] utf8Bytes = Encoding.UTF8.GetBytes(textoConAcentos);
byte[] asciiBytes = Encoding.ASCII.GetBytes(textoConAcentos);
byte[] latin1Bytes = Encoding.Latin1.GetBytes(textoConAcentos);
Console.WriteLine($"Texto original: {textoConAcentos}");
Console.WriteLine($"UTF-8: {utf8Bytes.Length} bytes");
Console.WriteLine($"ASCII: {asciiBytes.Length} bytes");
Console.WriteLine($"Latin1: {latin1Bytes.Length} bytes");
// Conversión de vuelta
string desdeUtf8 = Encoding.UTF8.GetString(utf8Bytes);
string desdeAscii = Encoding.ASCII.GetString(asciiBytes);
Console.WriteLine($"Desde UTF-8: {desdeUtf8}");
Console.WriteLine($"Desde ASCII: {desdeAscii}");
Ejemplo práctico: Procesador de texto simple
using System.Text;
using System.Text.RegularExpressions;
public class ProcesadorTexto
{
public static string LimpiarTexto(string texto)
{
if (string.IsNullOrWhiteSpace(texto))
return string.Empty;
// Eliminar múltiples espacios
texto = Regex.Replace(texto, @"\s+", " ");
// Limpiar espacios al inicio y final
return texto.Trim();
}
public static Dictionary<string, int> ContarPalabras(string texto)
{
var contador = new Dictionary<string, int>();
// Dividir en palabras, ignorando puntuación
string[] palabras = Regex.Split(texto.ToLower(), @"\W+")
.Where(p => !string.IsNullOrEmpty(p))
.ToArray();
foreach (string palabra in palabras)
{
if (contador.ContainsKey(palabra))
contador[palabra]++;
else
contador[palabra] = 1;
}
return contador;
}
public static string GenerarResumen(string texto, int longitudMaxima = 100)
{
if (texto.Length <= longitudMaxima)
return texto;
// Buscar el último espacio antes del límite
int ultimoEspacio = texto.LastIndexOf(' ', longitudMaxima);
if (ultimoEspacio > 0)
return texto.Substring(0, ultimoEspacio) + "...";
else
return texto.Substring(0, longitudMaxima) + "...";
}
}
// Uso del procesador
string textoEjemplo = @" Este es un texto de ejemplo para
demostrar las capacidades del procesador de texto que hemos creado.
Contiene múltiples espacios y saltos de línea innecesarios. ";
string textoLimpio = ProcesadorTexto.LimpiarTexto(textoEjemplo);
var contadorPalabras = ProcesadorTexto.ContarPalabras(textoLimpio);
string resumen = ProcesadorTexto.GenerarResumen(textoLimpio, 50);
Console.WriteLine("=== ANÁLISIS DE TEXTO ===");
Console.WriteLine($"Original: '{textoEjemplo}'");
Console.WriteLine($"Limpio: '{textoLimpio}'");
Console.WriteLine($"Resumen: {resumen}");
Console.WriteLine($"Total de palabras únicas: {contadorPalabras.Count}");
Console.WriteLine("\nPalabras más frecuentes:");
foreach (var palabra in contadorPalabras.OrderByDescending(p => p.Value).Take(5))
{
Console.WriteLine($"- '{palabra.Key}': {palabra.Value} veces");
}
Resumen
Las operaciones avanzadas con cadenas en C# nos proporcionan un conjunto completo de herramientas para manipular texto de manera eficiente y profesional. Hemos explorado desde técnicas básicas de formateo e interpolación hasta herramientas avanzadas como StringBuilder para construcción eficiente y expresiones regulares para análisis de patrones.
El dominio de estas técnicas es fundamental para crear aplicaciones robustas que manejen datos textuales con precisión. Las operaciones de búsqueda, transformación, validación y formato que hemos visto son esenciales en el desarrollo de interfaces de usuario, procesamiento de archivos, validación de datos y comunicación con servicios externos. En el siguiente artículo, comenzaremos a explorar la programación orientada a objetos, un paradigma que nos permitirá organizar y estructurar nuestro código de manera más elegante y mantenible.