Pregunta ¿Cómo contarías las ocurrencias de una cuerda (en realidad, un char) dentro de una cuerda?


Estoy haciendo algo en donde me di cuenta de que quería contar cuántos /s Pude encontrar en una cadena, y luego me di cuenta de que había varias formas de hacerlo, pero no podía decidir cuál era la mejor (o la más fácil).

En este momento voy con algo como:

string source = "/once/upon/a/time/";
int count = source.Length - source.Replace("/", "").Length;

Pero no me gusta para nada, ¿cualquier tomador?

Realmente no quiero excavar RegEx para esto, ¿verdad?

Sé que mi cadena va a tener el término que estoy buscando, por lo que puede suponer que ...

Por supuesto para cuerdas dónde  longitud> 1,

string haystack = "/once/upon/a/time";
string needle = "/";
int needleCount = ( haystack.Length - haystack.Replace(needle,"").Length ) / needle.Length;

696
2018-02-12 15:57


origen


Respuestas:


Si está usando .NET 3.5, puede hacerlo en un solo trazo con LINQ:

int count = source.Count(f => f == '/');

Si no quieres usar LINQ, puedes hacerlo con:

int count = source.Split('/').Length - 1;

¡Te sorprenderá saber que tu técnica original parece ser un 30% más rápida que cualquiera de estas! Acabo de hacer un punto de referencia rápido con "/ once / upon / a / time /" y los resultados son los siguientes:

Tu original = 12s
  source.Count = 19s
  source.Split = 17s
  para cada (de la respuesta de bobwienholt) = 10s

(Los tiempos son de 50,000,000 de iteraciones, por lo que es poco probable que note mucha diferencia en el mundo real).


809
2018-02-12 16:02



string source = "/once/upon/a/time/";
int count = 0;
foreach (char c in source) 
  if (c == '/') count++;

Tiene que ser más rápido que el source.Replace() por sí mismo.


144
2018-02-12 16:00



int count = new Regex(Regex.Escape(needle)).Matches(haystack).Count;

118
2017-12-10 15:54



Si desea poder buscar cadenas enteras, y no solo caracteres:

src.Select((c, i) => src.Substring(i)).Count(sub => sub.StartsWith(target))

Lea como "para cada personaje en la cadena, tome el resto de la cadena a partir de ese carácter como una subcadena, cuente si comienza con la cadena objetivo".


77
2018-02-12 16:26



Hice algunas investigaciones y descubrí que Richard Watson de la solución es más rápida en la mayoría de los casos. Esa es la tabla con los resultados de cada solución en la publicación (excepto aquellos que usan Regex porque arroja excepciones al analizar cadenas como "prueba {prueba")

    Name      | Short/char |  Long/char | Short/short| Long/short |  Long/long |
    Inspite   |         134|        1853|          95|        1146|         671|
    LukeH_1   |         346|        4490|         N/A|         N/A|         N/A|
    LukeH_2   |         152|        1569|         197|        2425|        2171|
Bobwienholt   |         230|        3269|         N/A|         N/A|         N/A|
Richard Watson|          33|         298|         146|         737|         543|
StefanosKargas|         N/A|         N/A|         681|       11884|       12486|

Puede ver que en caso de encontrar el número de ocurrencias de subcadenas cortas (1-5 caracteres) en cadenas cortas (10-50 caracteres), se prefiere el algoritmo original.

Además, para la subcadena multicharacter debe usar el siguiente código (basado en Richard Watson de solución)

int count = 0, n = 0;

if(substring != "")
{
    while ((n = source.IndexOf(substring, n, StringComparison.InvariantCulture)) != -1)
    {
        n += substring.Length;
        ++count;
    }
}

56
2017-08-02 08:27



LINQ funciona en todas las colecciones, y dado que las cadenas son solo una colección de personajes, ¿qué tal este pequeño e interesante trampolín?

var count = source.Count(c => c == '/');

Asegúrate de tener using System.Linq; en la parte superior de tu archivo de código, como .Count es un método de extensión de ese espacio de nombres.


49
2018-02-12 16:01



Estos dos solo funcionan para términos de búsqueda de un solo carácter ...

countOccurences("the", "the answer is the answer");

int countOccurences(string needle, string haystack)
{
    return (haystack.Length - haystack.Replace(needle,"").Length) / needle.Length;
}

puede ser mejor para agujas más largas ...

Pero tiene que haber una manera más elegante. :)


42
2018-02-12 16:04



string source = "/once/upon/a/time/";
int count = 0;
int n = 0;

while ((n = source.IndexOf('/', n)) != -1)
{
   n++;
   count++;
}

En mi computadora, es aproximadamente 2 segundos más rápido que la solución de cada personaje para 50 millones de iteraciones.

Revisión de 2013:

Cambie la cadena a un char [] e itere a través de eso. ¡Corta un segundo o dos más del tiempo total para iteraciones de 50m!

char[] testchars = source.ToCharArray();
foreach (char c in testchars)
{
     if (c == '/')
         count++;
}

Esto es aún más rápido:

char[] testchars = source.ToCharArray();
int length = testchars.Length;
for (int n = 0; n < length; n++)
{
    if (testchars[n] == '/')
        count++;
}

Para una buena medida, iterar desde el final de la matriz a 0 parece ser el más rápido, en aproximadamente un 5%.

int length = testchars.Length;
for (int n = length-1; n >= 0; n--)
{
    if (testchars[n] == '/')
        count++;
}

Me preguntaba por qué podría ser así y estaba buscando en Google (recuerdo algo sobre la iteración inversa siendo más rápido), y me encontré con esta pregunta SO que ya utiliza la técnica de cadena a char [] de manera molesta. Creo que el truco de inversión es nuevo en este contexto, sin embargo.

¿Cuál es la forma más rápida de iterar a través de caracteres individuales en una cadena en C #?


41
2018-05-14 20:10