¿Cómo contaría las apariciones de una cadena (en realidad, un carácter) dentro de una cadena?

Estoy haciendo algo 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).

Por el momento voy con algo como:

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

Pero no me gusta en absoluto, ¿algún interesado?

Realmente no quiero excavar RegEx para esto, ¿verdad?

Sé que mi cadena tendrá el término que estoy buscando, así que puedes asumir que ...

Por supuesto para cuerdas sin que importe longitud> 1,

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

preguntado el 12 de febrero de 09 a las 13:02

+1: debo decir que es una forma muy diferente de hacer el recuento. Estoy sorprendido por los resultados de las pruebas comparativas :) -

No es tan diferente ... es la forma típica de implementar esta funcionalidad en SQL: LEN(ColumnToCheck) - LEN(REPLACE(ColumnToCheck,"N","")). -

De hecho, debería dividir por "/". Longitud -

¿Puedo preguntar, cuáles dirían sus requisitos que debería ser el recuento para el número de apariciones de "//" dentro de "/////"? 2 o 4? -

usar expresiones regulares es probablemente la mejor manera de hacerlo -

30 Respuestas

Si está utilizando .NET 3.5, puede hacer esto en una sola línea con LINQ:

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

Si no desea utilizar LINQ, puede hacerlo con:

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

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

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

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

contestado el 23 de mayo de 17 a las 13:05

Sí, VS oculta los métodos de extensión LINQ en la clase de cadena. Supongo que pensaron que los desarrolladores no querrían que todos esos métodos de extensión aparecieran en la clase de cadena. Probablemente una sabia decisión. - Judá Gabriel Himango

Es posible que este comportamiento se deba a que VS2010 incluye automáticamente System.Linq en nuevos archivos de clase, VS2008 probablemente no lo haga. El espacio de nombres debe estar adentro para que intellisense funcione. - Sprague

Tenga en cuenta que las soluciones Count y Split solo funcionarán cuando esté contando caracteres. No funcionarán con cadenas, como lo hace la solución de OP. - Pedro Lillevold

f == '\' se trata de caracteres en una cadena, no cadenas en una cadena - Tomás Weller

Esto parece ser la respuesta a una pregunta diferente: "¿Cómo contarías las apariciones de un carácter dentro de una cadena?" - ben aaronson

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.

Respondido el 13 de Septiembre de 15 a las 20:09

Podría obtener una mejora marginal cambiando a for en lugar de foreach, pero solo un poquito. - Mark

No. La pregunta pide contar la ocurrencia de una cadena, no un carácter. - yuki sakura

Se trata de contar caracteres en una cadena. El título trata sobre contar cadenas en una cadena - Tomás Weller

@Mark Acabo de probarlo con un bucle for y en realidad fue más lento que usar foreach. ¿Podría ser debido a la verificación de límites? (El tiempo fue de 1.65 segundos frente a 2.05 en iteraciones de 5 mil). Medición

Si bien la pregunta es pedir una cadena dentro de una cadena, el problema de ejemplo que OP publicó es en realidad solo un carácter, en cuyo caso llamaría a esta respuesta una solución válida, ya que muestra una mejor manera (búsqueda de caracteres en lugar de búsqueda de cadenas) para abordar el problema en cuestión. - Chad

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

Respondido 04 Oct 13, 16:10

+1: en algunos casos, es posible que desee agregar RegexOptions.IgnoreCase. - Voluntad verdadera

¿No es esto increíblemente bajo? - Tomas Ayoub

La sobrecarga de expresiones regulares no es ideal, además de "Realmente no quiero investigar la expresión regular para esto, ¿verdad?" - Chad

puede que no quiera Regex.Escape(...) so new System.Text.RegularExpressions.Regex(needle).Matches(haystack).Count; - barlop

Elegí este porque puede buscar cadenas, no solo caracteres. - James en Indy

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

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

Lea como "para cada carácter de la cadena, tome el resto de la cadena a partir de ese carácter como una subcadena; cuéntelo si comienza con la cadena de destino".

contestado el 28 de mayo de 19 a las 18:05

No estoy seguro de cómo puedo explicarlo de una manera más clara que la descripción dada. ¿Qué es confuso? - mqp

SUPER LENTO! Lo probé en una página de html y tomó aproximadamente 2 minutos, en comparación con otros métodos en esta página que tomaron 2 segundos. La respuesta fue correcta; era demasiado lento para ser utilizable. - JohnB

de acuerdo, demasiado lento. Soy un gran admirador de las soluciones de estilo linq, pero esta simplemente no es viable. - Sprague

Tenga en cuenta que la razón por la que esto es tan lento es que crea n cadenas, por lo que asigna aproximadamente n ^ 2/2 bytes. - Peter crabtree

Se lanza OutOfMemoryException para mis 210000 caracteres de cadena. - formando

Investigué un poco y descubrí que Richard Watson 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 las 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 una cadena corta (10-50 caracteres), se prefiere el algoritmo original.

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

int count = 0, n = 0;

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

contestado el 23 de mayo de 17 a las 13:05

Estaba a punto de agregar mi propia solución de 'bajo nivel' (sin crear subcadenas, usando reemplazar / dividir o cualquier Regex / Linq), pero la suya es posiblemente incluso mejor que la mía (y al menos más corta). ¡Gracias! - dan w

Para las soluciones Regex, agregue un Regex.Escape(needle) - Timina

Solo para señalar a otros, el valor de búsqueda debe verificarse si está vacío, de lo contrario, entrará en un bucle infinito. - QuienEsRico

Tal vez sea solo yo, pero para source="aaa" substring="aa" Esperaba recuperar 2, no 1. Para "arreglar" esto, cambie n += substring.Length a n++ - ytoledano

puedes agregar el overlapped bandera para cumplir con su caso de esta manera: overlapped=True;.... if(overlapped) {++n;} else {n += substring.Length;} - tsionyx

LINQ funciona en todas las colecciones, y dado que las cadenas son solo una colección de caracteres, ¿qué tal esta pequeña y agradable línea?

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

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

Respondido 23 Feb 17, 23:02

¿Realmente vale la pena usar var allí? ¿Hay alguna posibilidad de que Count sea reemplazado por algo que no devuelva un int? - Que es eso

@Whatsit: puede escribir 'var' con solo su mano izquierda mientras que 'int' requiere ambas manos;) - Sean brillante

int todas las letras residen en las llaves de la casa, mientras que var no lo hace. uh ... espera, estoy usando Dvorak - Miguel Buen

@BDotA Asegúrese de tener un 'using System.Linq;' en la parte superior de su archivo. Además, intellisense podría ocultarle la llamada .Count ya que es una cadena. Aun así, se compilará y funcionará bien. - Judá Gabriel Himango

@JudahGabrielHimango Yo diría que se debería usar var especialmente cuando el tipo de variable es obvio (y por brevedad y consistencia) - EriF89

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 para cada carácter para 50 millones de iteraciones.

Revisión de 2013:

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

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++;
}

Por si acaso, la iteración desde el final de la matriz hasta 0 parece ser la más rápida, en aproximadamente un 5%.

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

Me preguntaba por qué esto podría ser y estaba buscando en Google (recuerdo algo acerca de que la iteración inversa es más rápida), y encontré esta pregunta SO que ya usa molestamente la técnica de cadena para char []. Sin embargo, creo que el truco de la inversión es nuevo en este contexto.

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

contestado el 23 de mayo de 17 a las 13:05

Podrías poner source.IndexOf('/', n + 1) y perder el n++ y los corchetes del while :) Además, ponga una variable string word = "/" en lugar del personaje. - NeeKo

Hola Niko, mira nuevas respuestas. Sin embargo, podría ser más difícil hacer una subcadena de longitud variable. - ricardo watson

Usé algo similar al pasar por la subcadena; eso es hasta que me di cuenta de que indexOf tiene un startIndex. La primera solución es la que más me gusta, ya que ofrece un buen equilibrio entre velocidad y espacio de memoria. - Samir Banjanovic

Leí en alguna parte que es más rápido iterar hacia atrás porque es más rápido comparar un valor con 0 - reggaeguitarra

@shitpoet sí. Si observa el código subyacente, es una llamada nativa. public char [] toCharArray () {... System.arraycopy (valor, 0, resultado, 0, valor.longitud); ...} - ricardo watson

Ambos 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 resultar mejor para agujas más largas ...

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

Respondido 12 Feb 09, 16:02

Para tener en cuenta los reemplazos de varios caracteres. Sin él, contar "el" en "la prueba es la clave" devolvería 6. - ZombieOvejas

Benchmarked y comparado con el string.Split-way - funciona aproximadamente 1.5 veces más rápido. Prestigio. - Alex

Edit:

source.Split('/').Length-1

Respondido 09 Abr '13, 19:04

Esto es lo que hago. Y source.Split(new[]{"//"}, StringSplitOptions.None).Count - 1 para separadores de varios caracteres. - bzlm

Esto realizaría al menos n asignaciones de cadenas en el montón, más (posiblemente) algunos cambios de tamaño de matriz, ¿y todo esto solo para obtener el recuento? Extremadamente ineficiente, no se escala bien y nunca debe usarse en ningún código importante. - Zar Shardan

En C #, un buen contador String SubString es este compañero inesperadamente complicado:

public static int CCount(String haystack, String needle)
{
    return haystack.Split(new[] { needle }, StringSplitOptions.None).Length - 1;
}

Respondido 14 Feb 17, 08:02

Buena solución, ¡y también funciona para cadenas (no solo char)! - CrisPf

Gracias, es muy fácil olvidar algunas de las sutilezas del manejo de cadenas al intercambiar idiomas, ¡como la mayoría de nosotros ha hecho en estos días! - David

-1 porque: ¿Conoce la diferencia entre Count () y Count o Length? Si alguien está usando Count () en lugar de Count o Length, me activan. Count () crea IEnumerator y luego pasa por todas las ocurrencias de IEnumerable, mientras que Count o Length ya son propiedades establecidas del objeto que ya contienen el recuento que desea sin la necesidad de iterar sobre todos los elementos. - Aerosón

Buen lugar, y lo extraño es que en mi biblioteca, de donde tomé la función, estoy usando "Longitud". Editado - David

Regex.Matches(input,  Regex.Escape("stringToMatch")).Count

contestado el 29 de mayo de 19 a las 08:05

Esto no es correcto si la entrada contiene caracteres especiales de expresiones regulares, es decir, | Debe haber un Regex.Escape (entrada) - Esben Skov Pedersen

En realidad, el stringToMatch necesita escapar, no el input. - Teodoro Zoulias

Estás en lo correcto. Arreglado. - cederlof

private int CountWords(string text, string word) {
    int count = (text.Length - text.Replace(word, "").Length) / word.Length;
    return count;
}

Debido a que la solución original era la más rápida para los caracteres, supongo que también lo será para las cadenas. Así que aquí está mi contribución.

Para el contexto: estaba buscando palabras como 'fallido' y 'exitoso' en un archivo de registro.

Gr, Ben

respondido 15 mar '11, 16:03

Simplemente no pase una cadena vacía para la variable "palabra" (división por error cero). - andres jens

string s = "65 fght 6565 4665 hjk";
int count = 0;
foreach (Match m in Regex.Matches(s, "65"))
  count++;

respondido 22 mar '12, 20:03

o Regex.Matches (s, "65"). Count ^ _ ^ - Meta

No funciona para todas las cuerdas. Intente buscar "++" en "abc ++ def ++ xyz" - meneo de pantano

Para cualquiera que desee un método de extensión String listo para usar,

esto es lo que uso, que se basó en la mejor de las respuestas publicadas:

public static class StringExtension
{    
    /// <summary> Returns the number of occurences of a string within a string, optional comparison allows case and culture control. </summary>
    public static int Occurrences(this System.String input, string value, StringComparison stringComparisonType = StringComparison.Ordinal)
    {
        if (String.IsNullOrEmpty(value)) return 0;

        int count    = 0;
        int position = 0;

        while ((position = input.IndexOf(value, position, stringComparisonType)) != -1)
        {
            position += value.Length;
            count    += 1;
        }

        return count;
    }

    /// <summary> Returns the number of occurences of a single character within a string. </summary>
    public static int Occurrences(this System.String input, char value)
    {
        int count = 0;
        foreach (char c in input) if (c == value) count += 1;
        return count;
    }
}

respondido 31 mar '15, 14:03

¿No se disparará el segundo método si la cadena pasada es nula o está vacía? Desde el punto de vista del estilo, ¿qué define la entrada como System.String en lugar de solo como cadena? - nodoide

public static int GetNumSubstringOccurrences(string text, string search)
{
    int num = 0;
    int pos = 0;

    if (!string.IsNullOrEmpty(text) && !string.IsNullOrEmpty(search))
    {
        while ((pos = text.IndexOf(search, pos)) > -1)
        {
            num ++;
            pos += search.Length;
        }
    }
    return num;
}

contestado el 18 de mayo de 15 a las 14:05

Creo que la forma más sencilla de hacerlo es utilizar las expresiones regulares. De esta manera, puede obtener el mismo recuento dividido que podría usar myVar.Split ('x') pero en una configuración de caracteres múltiples.

string myVar = "do this to count the number of words in my wording so that I can word it up!";
int count = Regex.Split(myVar, "word").Length;

contestado el 01 de mayo de 13 a las 17:05

string search = "/string";
var occurrences = (regex.Match(search, @"\/")).Count;

Esto contará cada vez que el programa encuentre "/ s" exactamente (distingue entre mayúsculas y minúsculas) y el número de ocurrencias de esto se almacenará en la variable "ocurrencias"

Respondido 06 Jul 17, 21:07

Sentí que nos faltaban ciertos tipos de recuento de subcadenas, como comparaciones inseguras byte por byte. Reuní el método del póster original y cualquier método que se me ocurriera.

Estas son las extensiones de cuerda que hice.

namespace Example
{
    using System;
    using System.Text;

    public static class StringExtensions
    {
        public static int CountSubstr(this string str, string substr)
        {
            return (str.Length - str.Replace(substr, "").Length) / substr.Length;
        }

        public static int CountSubstr(this string str, char substr)
        {
            return (str.Length - str.Replace(substr.ToString(), "").Length);
        }

        public static int CountSubstr2(this string str, string substr)
        {
            int substrlen = substr.Length;
            int lastIndex = str.IndexOf(substr, 0, StringComparison.Ordinal);
            int count = 0;
            while (lastIndex != -1)
            {
                ++count;
                lastIndex = str.IndexOf(substr, lastIndex + substrlen, StringComparison.Ordinal);
            }

            return count;
        }

        public static int CountSubstr2(this string str, char substr)
        {
            int lastIndex = str.IndexOf(substr, 0);
            int count = 0;
            while (lastIndex != -1)
            {
                ++count;
                lastIndex = str.IndexOf(substr, lastIndex + 1);
            }

            return count;
        }

        public static int CountChar(this string str, char substr)
        {
            int length = str.Length;
            int count = 0;
            for (int i = 0; i < length; ++i)
                if (str[i] == substr)
                    ++count;

            return count;
        }

        public static int CountChar2(this string str, char substr)
        {
            int count = 0;
            foreach (var c in str)
                if (c == substr)
                    ++count;

            return count;
        }

        public static unsafe int CountChar3(this string str, char substr)
        {
            int length = str.Length;
            int count = 0;
            fixed (char* chars = str)
            {
                for (int i = 0; i < length; ++i)
                    if (*(chars + i) == substr)
                        ++count;
            }

            return count;
        }

        public static unsafe int CountChar4(this string str, char substr)
        {
            int length = str.Length;
            int count = 0;
            fixed (char* chars = str)
            {
                for (int i = length - 1; i >= 0; --i)
                    if (*(chars + i) == substr)
                        ++count;
            }

            return count;
        }

        public static unsafe int CountSubstr3(this string str, string substr)
        {
            int length = str.Length;
            int substrlen = substr.Length;
            int count = 0;
            fixed (char* strc = str)
            {
                fixed (char* substrc = substr)
                {
                    int n = 0;

                    for (int i = 0; i < length; ++i)
                    {
                        if (*(strc + i) == *(substrc + n))
                        {
                            ++n;
                            if (n == substrlen)
                            {
                                ++count;
                                n = 0;
                            }
                        }
                        else
                            n = 0;
                    }
                }
            }

            return count;
        }

        public static int CountSubstr3(this string str, char substr)
        {
            return CountSubstr3(str, substr.ToString());
        }

        public static unsafe int CountSubstr4(this string str, string substr)
        {
            int length = str.Length;
            int substrLastIndex = substr.Length - 1;
            int count = 0;
            fixed (char* strc = str)
            {
                fixed (char* substrc = substr)
                {
                    int n = substrLastIndex;

                    for (int i = length - 1; i >= 0; --i)
                    {
                        if (*(strc + i) == *(substrc + n))
                        {
                            if (--n == -1)
                            {
                                ++count;
                                n = substrLastIndex;
                            }
                        }
                        else
                            n = substrLastIndex;
                    }
                }
            }

            return count;
        }

        public static int CountSubstr4(this string str, char substr)
        {
            return CountSubstr4(str, substr.ToString());
        }
    }
}

Seguido por el código de prueba ...

static void Main()
{
    const char matchA = '_';
    const string matchB = "and";
    const string matchC = "muchlongerword";
    const string testStrA = "_and_d_e_banna_i_o___pfasd__and_d_e_banna_i_o___pfasd_";
    const string testStrB = "and sdf and ans andeians andano ip and and sdf and ans andeians andano ip and";
    const string testStrC =
        "muchlongerword amuchlongerworsdfmuchlongerwordsdf jmuchlongerworijv muchlongerword sdmuchlongerword dsmuchlongerword";
    const int testSize = 1000000;
    Console.WriteLine(testStrA.CountSubstr('_'));
    Console.WriteLine(testStrA.CountSubstr2('_'));
    Console.WriteLine(testStrA.CountSubstr3('_'));
    Console.WriteLine(testStrA.CountSubstr4('_'));
    Console.WriteLine(testStrA.CountChar('_'));
    Console.WriteLine(testStrA.CountChar2('_'));
    Console.WriteLine(testStrA.CountChar3('_'));
    Console.WriteLine(testStrA.CountChar4('_'));
    Console.WriteLine(testStrB.CountSubstr("and"));
    Console.WriteLine(testStrB.CountSubstr2("and"));
    Console.WriteLine(testStrB.CountSubstr3("and"));
    Console.WriteLine(testStrB.CountSubstr4("and"));
    Console.WriteLine(testStrC.CountSubstr("muchlongerword"));
    Console.WriteLine(testStrC.CountSubstr2("muchlongerword"));
    Console.WriteLine(testStrC.CountSubstr3("muchlongerword"));
    Console.WriteLine(testStrC.CountSubstr4("muchlongerword"));
    var timer = new Stopwatch();
    timer.Start();
    for (int i = 0; i < testSize; ++i)
        testStrA.CountSubstr(matchA);
    timer.Stop();
    Console.WriteLine("CS1 chr: " + timer.Elapsed.TotalMilliseconds + "ms");

    timer.Restart();
    for (int i = 0; i < testSize; ++i)
        testStrB.CountSubstr(matchB);
    timer.Stop();
    Console.WriteLine("CS1 and: " + timer.Elapsed.TotalMilliseconds + "ms");

    timer.Restart();
    for (int i = 0; i < testSize; ++i)
        testStrC.CountSubstr(matchC);
    timer.Stop();
    Console.WriteLine("CS1 mlw: " + timer.Elapsed.TotalMilliseconds + "ms");

    timer.Restart();
    for (int i = 0; i < testSize; ++i)
        testStrA.CountSubstr2(matchA);
    timer.Stop();
    Console.WriteLine("CS2 chr: " + timer.Elapsed.TotalMilliseconds + "ms");

    timer.Restart();
    for (int i = 0; i < testSize; ++i)
        testStrB.CountSubstr2(matchB);
    timer.Stop();
    Console.WriteLine("CS2 and: " + timer.Elapsed.TotalMilliseconds + "ms");

    timer.Restart();
    for (int i = 0; i < testSize; ++i)
        testStrC.CountSubstr2(matchC);
    timer.Stop();
    Console.WriteLine("CS2 mlw: " + timer.Elapsed.TotalMilliseconds + "ms");

    timer.Restart();
    for (int i = 0; i < testSize; ++i)
        testStrA.CountSubstr3(matchA);
    timer.Stop();
    Console.WriteLine("CS3 chr: " + timer.Elapsed.TotalMilliseconds + "ms");

    timer.Restart();
    for (int i = 0; i < testSize; ++i)
        testStrB.CountSubstr3(matchB);
    timer.Stop();
    Console.WriteLine("CS3 and: " + timer.Elapsed.TotalMilliseconds + "ms");

    timer.Restart();
    for (int i = 0; i < testSize; ++i)
        testStrC.CountSubstr3(matchC);
    timer.Stop();
    Console.WriteLine("CS3 mlw: " + timer.Elapsed.TotalMilliseconds + "ms");

    timer.Restart();
    for (int i = 0; i < testSize; ++i)
        testStrA.CountSubstr4(matchA);
    timer.Stop();
    Console.WriteLine("CS4 chr: " + timer.Elapsed.TotalMilliseconds + "ms");

    timer.Restart();
    for (int i = 0; i < testSize; ++i)
        testStrB.CountSubstr4(matchB);
    timer.Stop();
    Console.WriteLine("CS4 and: " + timer.Elapsed.TotalMilliseconds + "ms");

    timer.Restart();
    for (int i = 0; i < testSize; ++i)
        testStrC.CountSubstr4(matchC);
    timer.Stop();
    Console.WriteLine("CS4 mlw: " + timer.Elapsed.TotalMilliseconds + "ms");

    timer.Restart();
    for (int i = 0; i < testSize; ++i)
        testStrA.CountChar(matchA);
    timer.Stop();
    Console.WriteLine("CC1 chr: " + timer.Elapsed.TotalMilliseconds + "ms");

    timer.Restart();
    for (int i = 0; i < testSize; ++i)
        testStrA.CountChar2(matchA);
    timer.Stop();
    Console.WriteLine("CC2 chr: " + timer.Elapsed.TotalMilliseconds + "ms");

    timer.Restart();
    for (int i = 0; i < testSize; ++i)
        testStrA.CountChar3(matchA);
    timer.Stop();
    Console.WriteLine("CC3 chr: " + timer.Elapsed.TotalMilliseconds + "ms");

    timer.Restart();
    for (int i = 0; i < testSize; ++i)
        testStrA.CountChar4(matchA);
    timer.Stop();
    Console.WriteLine("CC4 chr: " + timer.Elapsed.TotalMilliseconds + "ms");
}

Resultados: CSX se corresponde con CountSubstrX y CCX se corresponde con CountCharX. "chr" busca una cadena para '_', "y" busca una cadena para "y", y "mlw" busca una cadena para "muchlongerword"

CS1 chr: 824.123ms
CS1 and: 586.1893ms
CS1 mlw: 486.5414ms
CS2 chr: 127.8941ms
CS2 and: 806.3918ms
CS2 mlw: 497.318ms
CS3 chr: 201.8896ms
CS3 and: 124.0675ms
CS3 mlw: 212.8341ms
CS4 chr: 81.5183ms
CS4 and: 92.0615ms
CS4 mlw: 116.2197ms
CC1 chr: 66.4078ms
CC2 chr: 64.0161ms
CC3 chr: 65.9013ms
CC4 chr: 65.8206ms

Y finalmente, tenía un archivo con 3.6 millones de caracteres. Fue "derp adfderdserp dfaerpderp deasderp" repetido 100,000 veces. Busqué "derp" dentro del archivo con los métodos anteriores 100 veces estos resultados.

CS1Derp: 1501.3444ms
CS2Derp: 1585.797ms
CS3Derp: 376.0937ms
CS4Derp: 271.1663ms

Entonces, mi cuarto método es definitivamente el ganador, pero, de manera realista, si un archivo de 4 millones de caracteres 3.6 veces solo tomó 100ms como el peor de los casos, entonces todo esto es bastante insignificante.

Por cierto, también busqué el carácter 'd' en el archivo de 3.6 millones de caracteres con 100 veces los métodos CountSubstr y CountChar. Resultados ...

CS1  d : 2606.9513ms
CS2  d : 339.7942ms
CS3  d : 960.281ms
CS4  d : 233.3442ms
CC1  d : 302.4122ms
CC2  d : 280.7719ms
CC3  d : 299.1125ms
CC4  d : 292.9365ms

El método de los carteles originales es muy malo para las agujas de un solo carácter en un pajar grande de acuerdo con esto.

Nota: Todos los valores se actualizaron a la salida de la versión de lanzamiento. Accidentalmente olvidé construir en el modo de lanzamiento la primera vez que publiqué esto. Algunas de mis declaraciones se han modificado.

Respondido el 08 de diciembre de 18 a las 11:12

Gracias por los resultados de rendimiento. Una diferencia de factor en la velocidad de 10 podría ser una razón para no considerar un linq u otra solución cuidadosamente escrita, sino optar por un método de extensión. - Andreas Reiff

Una función genérica para ocurrencias de cadenas:

public int getNumberOfOccurencies(String inputString, String checkString)
{
    if (checkString.Length > inputString.Length || checkString.Equals("")) { return 0; }
    int lengthDifference = inputString.Length - checkString.Length;
    int occurencies = 0;
    for (int i = 0; i < lengthDifference; i++) {
        if (inputString.Substring(i, checkString.Length).Equals(checkString)) { occurencies++; i += checkString.Length - 1; } }
    return occurencies;
}

respondido 10 mar '12, 13:03

Esto crea una GRAN cantidad de cadenas temporales y hace que el recolector de basura trabaje muy duro. - EricLaw

string source = "/once/upon/a/time/";
int count = 0, n = 0;
while ((n = source.IndexOf('/', n) + 1) != 0) count++;

Una variación de la respuesta de Richard Watson, un poco más rápida con la mejora de la eficiencia cuantas más veces se produce el carácter en la cadena, ¡y menos código!

Aunque debo decir, sin probar exhaustivamente todos los escenarios, vi una mejora de velocidad muy significativa al usar:

int count = 0;
for (int n = 0; n < source.Length; n++) if (source[n] == '/') count++;

Respondido el 25 de enero de 13 a las 16:01

            var conditionalStatement = conditionSetting.Value;

            //order of replace matters, remove == before =, incase of ===
            conditionalStatement = conditionalStatement.Replace("==", "~").Replace("!=", "~").Replace('=', '~').Replace('!', '~').Replace('>', '~').Replace('<', '~').Replace(">=", "~").Replace("<=", "~");

            var listOfValidConditions = new List<string>() { "!=", "==", ">", "<", ">=", "<=" };

            if (conditionalStatement.Count(x => x == '~') != 1)
            {
                result.InvalidFieldList.Add(new KeyFieldData(batch.DECurrentField, "The IsDoubleKeyCondition does not contain a supported conditional statement. Contact System Administrator."));
                result.Status = ValidatorStatus.Fail;
                return result;
            }

Necesitaba hacer algo similar para probar declaraciones condicionales de una cadena.

Reemplazó lo que estaba buscando con un solo carácter y conté las instancias del solo carácter.

Obviamente, será necesario verificar que el carácter único que está utilizando no exista en la cadena antes de que esto suceda para evitar recuentos incorrectos.

respondido 29 mar '13, 19:03

Cadena en cadena:

Busque "etc" en ".. JD JD JD JD etc. y etc. JDJDJDJDJDJDJDJD y etc."

var strOrigin = " .. JD JD JD JD etc. and etc. JDJDJDJDJDJDJDJD and etc.";
var searchStr = "etc";
int count = (strOrigin.Length - strOrigin.Replace(searchStr, "").Length)/searchStr.Length.

Verifique el rendimiento antes de descartar este como poco sólido / torpe ...

respondido 11 nov., 14:12

Mi toma inicial me dio algo como:

public static int CountOccurrences(string original, string substring)
{
    if (string.IsNullOrEmpty(substring))
        return 0;
    if (substring.Length == 1)
        return CountOccurrences(original, substring[0]);
    if (string.IsNullOrEmpty(original) ||
        substring.Length > original.Length)
        return 0;
    int substringCount = 0;
    for (int charIndex = 0; charIndex < original.Length; charIndex++)
    {
        for (int subCharIndex = 0, secondaryCharIndex = charIndex; subCharIndex < substring.Length && secondaryCharIndex < original.Length; subCharIndex++, secondaryCharIndex++)
        {
            if (substring[subCharIndex] != original[secondaryCharIndex])
                goto continueOuter;
        }
        if (charIndex + substring.Length > original.Length)
            break;
        charIndex += substring.Length - 1;
        substringCount++;
    continueOuter:
        ;
    }
    return substringCount;
}

public static int CountOccurrences(string original, char @char)
{
    if (string.IsNullOrEmpty(original))
        return 0;
    int substringCount = 0;
    for (int charIndex = 0; charIndex < original.Length; charIndex++)
        if (@char == original[charIndex])
            substringCount++;
    return substringCount;
}

La aguja en un enfoque de pajar usando reemplazo y división produce más de 21 segundos, mientras que esto toma alrededor de 15.2.

Edite después de agregar un poco que agregaría substring.Length - 1 al charIndex (como debería), está en 11.6 segundos.

Edición 2: utilicé una cadena que tenía 26 cadenas de dos caracteres, aquí están los tiempos actualizados a los mismos textos de muestra:

Aguja en un pajar (versión OP): 7.8 Segundos

Mecanismo sugerido: 4.6 segundos.

Edición 3: agregando el caso de esquina de un solo carácter, pasó a 1.2 segundos.

Edición 4: Para el contexto: se utilizaron 50 millones de iteraciones.

contestado el 29 de mayo de 15 a las 07:05

Pensé que lanzaría mi método de extensión al ring (ver comentarios para más información). No he realizado ninguna evaluación comparativa formal, pero creo que tiene que ser muy rápido para la mayoría de los escenarios.

EDITAR: OK, entonces esta pregunta SO me hizo preguntarme cómo el rendimiento de nuestra implementación actual se compararía con algunas de las soluciones presentadas aquí. Decidí hacer una pequeña evaluación comparativa y descubrí que nuestra solución estaba muy en consonancia con el rendimiento de la solución proporcionada por ricardo watson hasta que realice una búsqueda agresiva con cadenas grandes (100 Kb +), subcadenas grandes (32 Kb +) y muchas repeticiones incrustadas (10K +). En ese momento, nuestra solución era entre 2 y 4 veces más lenta. Dado esto y el hecho de que realmente nos gusta la solución presentada por Richard Watson, hemos refactorizado nuestra solución en consecuencia. Solo quería que esto estuviera disponible para cualquiera que pudiera beneficiarse de él.

Nuestra solución original:

    /// <summary>
    /// Counts the number of occurrences of the specified substring within
    /// the current string.
    /// </summary>
    /// <param name="s">The current string.</param>
    /// <param name="substring">The substring we are searching for.</param>
    /// <param name="aggressiveSearch">Indicates whether or not the algorithm 
    /// should be aggressive in its search behavior (see Remarks). Default 
    /// behavior is non-aggressive.</param>
    /// <remarks>This algorithm has two search modes - aggressive and 
    /// non-aggressive. When in aggressive search mode (aggressiveSearch = 
    /// true), the algorithm will try to match at every possible starting 
    /// character index within the string. When false, all subsequent 
    /// character indexes within a substring match will not be evaluated. 
    /// For example, if the string was 'abbbc' and we were searching for 
    /// the substring 'bb', then aggressive search would find 2 matches 
    /// with starting indexes of 1 and 2. Non aggressive search would find 
    /// just 1 match with starting index at 1. After the match was made, 
    /// the non aggressive search would attempt to make it's next match 
    /// starting at index 3 instead of 2.</remarks>
    /// <returns>The count of occurrences of the substring within the string.</returns>
    public static int CountOccurrences(this string s, string substring, 
        bool aggressiveSearch = false)
    {
        // if s or substring is null or empty, substring cannot be found in s
        if (string.IsNullOrEmpty(s) || string.IsNullOrEmpty(substring))
            return 0;

        // if the length of substring is greater than the length of s,
        // substring cannot be found in s
        if (substring.Length > s.Length)
            return 0;

        var sChars = s.ToCharArray();
        var substringChars = substring.ToCharArray();
        var count = 0;
        var sCharsIndex = 0;

        // substring cannot start in s beyond following index
        var lastStartIndex = sChars.Length - substringChars.Length;

        while (sCharsIndex <= lastStartIndex)
        {
            if (sChars[sCharsIndex] == substringChars[0])
            {
                // potential match checking
                var match = true;
                var offset = 1;
                while (offset < substringChars.Length)
                {
                    if (sChars[sCharsIndex + offset] != substringChars[offset])
                    {
                        match = false;
                        break;
                    }
                    offset++;
                }
                if (match)
                {
                    count++;
                    // if aggressive, just advance to next char in s, otherwise, 
                    // skip past the match just found in s
                    sCharsIndex += aggressiveSearch ? 1 : substringChars.Length;
                }
                else
                {
                    // no match found, just move to next char in s
                    sCharsIndex++;
                }
            }
            else
            {
                // no match at current index, move along
                sCharsIndex++;
            }
        }

        return count;
    }

Y aquí está nuestra solución revisada:

    /// <summary>
    /// Counts the number of occurrences of the specified substring within
    /// the current string.
    /// </summary>
    /// <param name="s">The current string.</param>
    /// <param name="substring">The substring we are searching for.</param>
    /// <param name="aggressiveSearch">Indicates whether or not the algorithm 
    /// should be aggressive in its search behavior (see Remarks). Default 
    /// behavior is non-aggressive.</param>
    /// <remarks>This algorithm has two search modes - aggressive and 
    /// non-aggressive. When in aggressive search mode (aggressiveSearch = 
    /// true), the algorithm will try to match at every possible starting 
    /// character index within the string. When false, all subsequent 
    /// character indexes within a substring match will not be evaluated. 
    /// For example, if the string was 'abbbc' and we were searching for 
    /// the substring 'bb', then aggressive search would find 2 matches 
    /// with starting indexes of 1 and 2. Non aggressive search would find 
    /// just 1 match with starting index at 1. After the match was made, 
    /// the non aggressive search would attempt to make it's next match 
    /// starting at index 3 instead of 2.</remarks>
    /// <returns>The count of occurrences of the substring within the string.</returns>
    public static int CountOccurrences(this string s, string substring, 
        bool aggressiveSearch = false)
    {
        // if s or substring is null or empty, substring cannot be found in s
        if (string.IsNullOrEmpty(s) || string.IsNullOrEmpty(substring))
            return 0;

        // if the length of substring is greater than the length of s,
        // substring cannot be found in s
        if (substring.Length > s.Length)
            return 0;

        int count = 0, n = 0;
        while ((n = s.IndexOf(substring, n, StringComparison.InvariantCulture)) != -1)
        {
            if (aggressiveSearch)
                n++;
            else
                n += substring.Length;
            count++;
        }

        return count;
    }

Respondido el 27 de diciembre de 19 a las 19:12

string Name = "Very good nice one is very good but is very good nice one this is called the term";
bool valid=true;
int count = 0;
int k=0;
int m = 0;
while (valid)
{
    k = Name.Substring(m,Name.Length-m).IndexOf("good");
    if (k != -1)
    {
        count++;
        m = m + k + 4;
    }
    else
        valid = false;
}
Console.WriteLine(count + " Times accures");

Respondido 04 Abr '12, 10:04

string s = "HOWLYH THIS ACTUALLY WORKSH WOWH";
int count = 0;
for (int i = 0; i < s.Length; i++)
   if (s[i] == 'H') count++;

Simplemente verifica cada carácter en la cadena, si el carácter es el carácter que está buscando, agregue uno para contar.

Respondido el 14 de junio de 14 a las 19:06

Si echa un vistazo a esta página web, Se comparan 15 formas diferentes de hacer esto, incluido el uso de bucles paralelos.

La forma más rápida parece ser usar un bucle for de un solo subproceso (si tiene .Net versión <4.0) o un bucle for paralelo (si usa .Net> 4.0 con miles de comprobaciones).

Suponiendo que "ss" es su cadena de búsqueda, "ch" es su matriz de caracteres (si tiene más de un carácter que está buscando), aquí está la esencia básica del código que tuvo el tiempo de ejecución más rápido de un solo subproceso:

for (int x = 0; x < ss.Length; x++)
{
    for (int y = 0; y < ch.Length; y++)
    {
        for (int a = 0; a < ss[x].Length; a++ )
        {
        if (ss[x][a] == ch[y])
            //it's found. DO what you need to here.
        }
    }
}

También se proporciona el código fuente de referencia para que pueda ejecutar sus propias pruebas.

Respondido 16 ago 14, 14:08

str="aaabbbbjjja";
int count = 0;
int size = str.Length;

string[] strarray = new string[size];
for (int i = 0; i < str.Length; i++)
{
    strarray[i] = str.Substring(i, 1);
}
Array.Sort(strarray);
str = "";
for (int i = 0; i < strarray.Length - 1; i++)
{

    if (strarray[i] == strarray[i + 1])
    {

        count++;
    }
    else
    {
        count++;
        str = str + strarray[i] + count;
        count = 0;
    }

}
count++;
str = str + strarray[strarray.Length - 1] + count;

Esto es para contar la ocurrencia de caracteres. Para este ejemplo, la salida será "a4b4j3".

contestado el 03 de mayo de 16 a las 13:05

Sin contar las apariciones de una cadena, sino contar más caracteres: ¿qué tal una forma de especificar cuál es la cadena que debe coincidir con Narenda? - Paul Sullivan

int count = 0; string str = "tenemos foo y foo por favor cuente foo en esto"; string stroccurance = "foo"; cadena [] strarray = str.Split (''); Array.Sort (strarray); str = ""; for (int i = 0; i <strarray.Length - 1; i ++) {if (strarray [i] == stroccurance) {count ++; }} str = "Número de ocurrencia de" + stroccurance + "es" + cuenta; A través de esto, puede contar cualquier ocurrencia de cadena en este ejemplo. Estoy contando la ocurrencia de "foo" y me dará la salida 3. - narendra kumar

Para el caso de un delimitador de cadena (no para el caso de caracteres, como dice el sujeto):
string source = "@@@ once @@@ sobre @@@ a @@@ time @@@";
int count = source.Split (new [] {"@@@"}, StringSplitOptions.RemoveEmptyEntries) .Length - 1;

El delimitador natural del valor de fuente original del cartel ("/ once / upon / a / time /") es un char '/' y las respuestas explican la opción source.Split (char []) aunque ...

respondido 17 nov., 17:20

usando System.Linq;

int CountOf => "A :: BC :: D" .Split ("::"). Longitud - 1;

contestado el 30 de mayo de 19 a las 19:05

No es la respuesta que estás buscando? Examinar otras preguntas etiquetadas or haz tu propia pregunta.