Question Comment comptez-vous les occurrences d'une chaîne (en fait un caractère) dans une chaîne?


Je fais quelque chose où je me suis rendu compte que je voulais compter combien /s Je pouvais trouver dans une ficelle, et puis cela m'a frappé, qu'il y avait plusieurs façons de le faire, mais je ne pouvais pas décider de ce qui était le meilleur (ou le plus facile).

En ce moment je vais avec quelque chose comme:

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

Mais je ne l'aime pas du tout, les preneurs?

Je ne veux pas vraiment déterrer RegEx pour cela, est-ce que je

Je sais que ma chaîne va avoir le terme que je cherche, donc vous pouvez supposer que ...

Bien sûr pour les cordes   longueur> 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


origine


Réponses:


Si vous utilisez .NET 3.5, vous pouvez le faire dans un interligne avec LINQ:

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

Si vous ne voulez pas utiliser LINQ, vous pouvez le faire avec:

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

Vous pourriez être surpris d'apprendre que votre technique originale semble être environ 30% plus rapide que l'une ou l'autre! Je viens de faire un benchmark rapide avec "/ once / upon / a / time /" et les résultats sont les suivants:

Votre original = 12s
  source.Count = 19s
  source.Split = 17s
  pour chaque (de la réponse de bobwienholt) = 10s

(Les temps sont pour 50 000 000 itérations donc vous ne remarquerez probablement pas beaucoup de différence dans le monde réel.)


809
2018-02-12 16:02



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

Doit être plus rapide que le source.Replace() par lui-même.


144
2018-02-12 16:00



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

118
2017-12-10 15:54



Si vous voulez pouvoir rechercher des chaînes entières, et pas seulement des caractères:

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

Lire comme "pour chaque caractère de la chaîne, prendre le reste de la chaîne à partir de ce caractère comme une sous-chaîne, comptez-le s'il commence par la chaîne cible."


77
2018-02-12 16:26



J'ai fait des recherches et j'ai trouvé que Richard Watson la solution est la plus rapide dans la plupart des cas. C'est le tableau avec les résultats de chaque solution dans le poste (sauf ceux qui utilisent Regex car il lance des exceptions en analysant la chaîne comme "test {test")

    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|

Vous pouvez voir que dans le cas de trouver le nombre d'occurrences de sous-chaînes courtes (1-5 caractères) en chaîne courte (10-50 caractères) l'algorithme original est préféré.

En outre, pour la sous-chaîne multicharacters, vous devez utiliser le code suivant (basé sur Richard Watson Solution)

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 fonctionne sur toutes les collections, et puisque les chaînes ne sont qu'une collection de caractères, que diriez-vous de ce joli petit one-liner:

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

Assurez-vous que vous avez using System.Linq; en haut de votre fichier de code, comme .Count est une méthode d'extension de cet espace de noms.


49
2018-02-12 16:01



Ces deux ne fonctionnent que pour les termes de recherche à un seul caractère ...

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

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

peut s'avérer être meilleur pour des aiguilles plus longues ...

Mais il doit y avoir une manière plus élégante. :)


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

Sur mon ordinateur, il est environ 2 secondes plus rapide que la solution pour tous les caractères pour 50 millions d'itérations.

Révision 2013:

Changez la chaîne en char [] et passez en revue cela. Coupe encore une seconde ou deux du temps total pour des itérations de 50m!

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

C'est encore plus rapide:

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

Pour faire bonne mesure, l'itération de la fin du tableau à 0 semble être la plus rapide, d'environ 5%.

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

Je me demandais pourquoi cela pouvait être et que je faisais des recherches sur Googling (je me souviens de quelque chose à propos de l'itération inverse en étant plus rapide), et je suis tombé sur cette question SO qui utilise déjà la technique de char []. Je pense que le tour d'inversion est nouveau dans ce contexte, cependant.

Quel est le moyen le plus rapide de parcourir des caractères individuels dans une chaîne en C #?


41
2018-05-14 20:10



Modifier:

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

17
2018-02-12 18:48



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

14
2018-06-19 10:49