¿Cuántas veces aparece la letra «a» en la palabra «abracadabra»? Resolvemos el problema en C#
Imagina: estás escribiendo un analizador de logs, parseando un archivo CSV o simplemente verificando cuántas veces el usuario ingresó el carácter @ en un correo electrónico. La tarea de «contar el número de ocurrencias de un carácter o subcadena en una cadena» es una de esas que aparece en cada segundo proyecto. En Stack Overflow, esta pregunta ha acumulado miles de visitas y docenas de respuestas. No es de extrañar: en C# hay al menos 5 formas diferentes, y elegir la correcta puede ahorrarte horas de depuración.
En este artículo, analizaremos todos los métodos populares, desde el clásico bucle hasta los modernos Span<T> y LINQ. Descubrirás qué método es más rápido, cuál es más legible y cuál es mejor no usar en producción. ¡Vamos allá!
1. Bucle clásico foreach — el método del abuelo
La forma más intuitiva: tomamos la cadena, recorremos cada carácter y contamos las coincidencias. Funciona siempre, no requiere bibliotecas adicionales.
string text = "abracadabra";char target = 'a';int count = 0;
foreach (char c in text){ if (c == target) count++;}
Console.WriteLine($"El carácter '{target}' aparece {count} vez/veces."); // Salida: 4Ventajas: legibilidad, control sobre el proceso, se puede añadir lógica (por ejemplo, ignorar mayúsculas/minúsculas).
Desventajas: no es adecuado para contar subcadenas (varios caracteres seguidos) — para eso habría que escribir un bucle anidado.
2. Método Count() de LINQ — una línea para caracteres
Si te gusta el código conciso y usas LINQ, esta opción es para ti. El método Count() acepta un predicado y devuelve el número de elementos que cumplen la condición.
using System.Linq;
string text = "Hello, World!";char target = 'l';
int count = text.Count(c => c == target);Console.WriteLine(count); // Salida: 3Ten en cuenta: Count() es un método de extensión del espacio de nombres System.Linq. Si no has incluido using System.Linq, el código no se compilará.
Para contar subcadenas, este método no es adecuado directamente. Tendrías que dividir primero la cadena o usar expresiones regulares (ver punto 5).
3. Span<T> y MemoryExtensions — rendimiento al máximo
Si trabajas con cadenas grandes (megabytes de logs) y cada milisegundo cuenta, olvídate de LINQ. Usa Span<char> y el método Count() de MemoryExtensions (disponible desde .NET 5+).
using System;
string text = "abracadabra";ReadOnlySpan<char> span = text.AsSpan();int count = span.Count('a');
Console.WriteLine(count); // Salida: 4¿Por qué es rápido? Span<T> trabaja con bloques de memoria contiguos sin asignar nuevos objetos. El método Count() en MemoryExtensions está optimizado con instrucciones SIMD: procesa de 16 a 32 caracteres por ciclo de CPU.
Para subcadenas también hay solución:
int count = span.Count("ra"); // cuenta las ocurrencias de la subcadena "ra"Console.WriteLine(count); // Salida: 2 ("abracadabra" contiene "ra" dos veces)Importante: este método distingue entre mayúsculas y minúsculas. Si necesitas ignorarlas, primero convierte la cadena a un solo caso o usa span.Count("ra", StringComparison.OrdinalIgnoreCase).
4. Método Split — un truco astuto para subcadenas
Un método antiguo pero funcional: dividir la cadena por la subcadena buscada y contar el número de partes menos uno.
string text = "uno, dos, tres, uno, cuatro";string target = "uno";
int count = text.Split(target, StringSplitOptions.None).Length - 1;Console.WriteLine(count); // Salida: 2¿Cómo funciona? Split