Je fais quelque chose pour lequel je me suis rendu compte que je voulais compter le nombre de /
que je pouvais trouver dans une chaîne, puis il m'a semblé qu'il y avait plusieurs façons de le faire, mais je ne pouvais pas décider du meilleur ( ou plus facile) était.
En ce moment, je vais avec quelque chose comme:
string source = "/once/upon/a/time/";
int count = source.Length - source.Replace("/", "").Length;
Mais je n'aime pas du tout, des preneurs?
Je ne veux pas vraiment creuser RegEx
pour ça, n'est-ce pas?
Je sais que ma chaîne va avoir le terme que je cherche, alors vous pouvez supposer que ...
Bien sûr pour les chaînes où longueur> 1,
string haystack = "/once/upon/a/time";
string needle = "/";
int needleCount = ( haystack.Length - haystack.Replace(needle,"").Length ) / needle.Length;
Si vous utilisez .NET 3.5, vous pouvez le faire dans une ligne 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 serez peut-être surpris d'apprendre que votre technique initiale semble être environ 30% plus rapide que l'une ou l'autre! Je viens de faire un repère rapide avec "/ une fois/après/un/temps /" et les résultats sont les suivants:
Votre original = 12s
source.Count = 19s
source.Split = 17s
foreach ( de la réponse de bobwienholt ) = 10s
(Les temps sont pour 50 000 000 itérations, il est donc peu probable que vous remarquiez une grande différence dans le monde réel.)
string source = "/once/upon/a/time/";
int count = 0;
foreach (char c in source)
if (c == '/') count++;
Doit être plus rapide que la source.Replace()
par elle-même.
int count = new Regex(Regex.Escape(needle)).Matches(haystack).Count;
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, prenez 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".
J'ai fait des recherches et trouvé que Richard Watson la solution est la plus rapide dans la plupart des cas. C'est la table avec les résultats de chaque solution de la publication (sauf ceux qui utilisent Regex car ils lèvent des exceptions lors de l'analyse d'une chaîne telle que "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 constater qu'en cas de recherche du nombre d'occurrences de sous-chaînes courtes (1 à 5 caractères) dans une chaîne courte (10 à 50 caractères), l'algorithme d'origine est préféré.
De plus, pour les sous-chaînes multicaractères, vous devez utiliser le code suivant (basé sur la solution Richard Watson )
int count = 0, n = 0;
if(substring != "")
{
while ((n = source.IndexOf(substring, n, StringComparison.InvariantCulture)) != -1)
{
n += substring.Length;
++count;
}
}
LINQ fonctionne sur toutes les collections, et comme 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, car .Count
est une méthode d'extension de cet espace de noms.
string source = "/once/upon/a/time/";
int count = 0;
int n = 0;
while ((n = source.IndexOf('/', n)) != -1)
{
n++;
count++;
}
Sur mon ordinateur, c'est environ 2 secondes plus rapide que la solution à chaque caractère pour 50 millions d'itérations.
Révision de 2013:
Changez la chaîne en un caractère [] et parcourez-le. Réduit encore une ou deux secondes du temps total pour 50 millions d'itérations!
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, itérer de la fin du tableau à 0 semble être le 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 cherchait dans Google (je me souviens de quelque chose à propos de la répétition inversée plus rapide), et suis tombé sur cette question SO qui utilise déjà de manière agaçante la technique string to char []. Je pense cependant que l’astuce de renversement est nouvelle dans ce contexte.
Quel est le moyen le plus rapide de parcourir différents caractères dans une chaîne en C #?
Ces deux ne fonctionnent que pour les termes de recherche à caractère unique ...
countOccurences("the", "the answer is the answer");
int countOccurences(string needle, string haystack)
{
return (haystack.Length - haystack.Replace(needle,"").Length) / needle.Length;
}
peut se révéler mieux pour les aiguilles plus longues ...
Mais il doit y avoir un moyen plus élégant. :)
Edit:
source.Split('/').Length-1
En C #, un compteur Nice String SubString est ce type compliqué inattendu:
public static int CCount(String haystack, String needle)
{
return haystack.Split(new[] { needle }, StringSplitOptions.None).Length - 1;
}
Regex.Matches(input, Regex.Escape("stringToMatch")).Count
private int CountWords(string text, string Word) {
int count = (text.Length - text.Replace(Word, "").Length) / Word.Length;
return count;
}
Parce que la solution originale était la plus rapide pour les caractères, je suppose que ce sera également pour les chaînes. Alors voici ma contribution.
Pour le contexte: je cherchais des mots comme "échec" et "réussi" dans un fichier journal.
Gr, Ben
string s = "65 fght 6565 4665 hjk";
int count = 0;
foreach (Match m in Regex.Matches(s, "65"))
count++;
Pour tous ceux qui veulent une méthode d’extension prête à l’emploi,
voici ce que j'utilise, basé sur les meilleures réponses publiées:
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;
}
}
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;
}
Je pense que le moyen le plus simple de procéder consiste à utiliser les expressions régulières. De cette façon, vous pouvez obtenir le même nombre de partages que vous pouvez utiliser myVar.Split ('x') mais dans un paramètre à plusieurs caractères.
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;
string search = "/string";
var occurrences = (regex.Match(search, @"\/")).Count;
Cela comptera chaque fois que le programme trouvera "/ s" exactement (sensible à la casse) et le nombre d’occurrences de cela sera stocké dans la variable "occurrences"
Chaîne en chaîne:
Trouvez "etc" dans ".. JD JD JD JD etc. et etc. JDJDJDJDJDJDJDJD et 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.
Vérifiez les performances avant de jeter celui-ci, car il est maladroit/maladroit ...
string source = "/once/upon/a/time/";
int count = 0, n = 0;
while ((n = source.IndexOf('/', n) + 1) != 0) count++;
Une variante de la réponse de Richard Watson, légèrement plus rapide et plus efficace, plus le caractère est multiple, et moins de code!
Bien que je dois dire que, sans avoir testé tous les scénarios, j'ai constaté une amélioration très significative de la vitesse en utilisant:
int count = 0;
for (int n = 0; n < source.Length; n++) if (source[n] == '/') count++;
Je sentais que nous manquions de certains types de comptage de sous-chaînes, comme les comparaisons non sécurisées octet par octet. J'ai mis en place la méthode de l'affiche originale et toutes les méthodes auxquelles je pouvais penser.
Ce sont les extensions de chaîne que j'ai faites.
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());
}
}
}
Suivi du code de test ...
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");
}
Résultats: CSX correspond à CountSubstrX et CCX correspond à CountCharX. "chr" recherche une chaîne pour '_', "et" recherche une chaîne pour "et", et "mlw" recherche une chaîne pour "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
Et finalement, j'ai eu un fichier avec 3,6 millions de caractères. C'était "derp adfderdserp dfaerpderp deasderp" répété 100 000 fois. J'ai cherché "derp" dans le fichier avec les méthodes ci-dessus 100 fois ces résultats.
CS1Derp: 1501.3444ms
CS2Derp: 1585.797ms
CS3Derp: 376.0937ms
CS4Derp: 271.1663ms
Donc, ma 4ème méthode est définitivement gagnante, mais, de façon réaliste, si un fichier de 3,6 millions de caractères 100 fois ne prenait que 1586ms comme pire des cas, tout cela est plutôt négligeable.
À propos, j’ai également recherché le caractère 'd' dans le fichier de 3,6 millions de caractères avec les méthodes 100 fois CountSubstr et CountChar. Résultats...
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
La méthode des affiches originales est très mauvaise pour les aiguilles à un seul caractère dans une grosse botte de foin selon ce qui précède.
Remarque: Toutes les valeurs ont été mises à jour dans la version finale de la version. J'ai accidentellement oublié de miser sur le mode Release lors de la première publication de ce message. Certaines de mes déclarations ont été modifiées.
Une fonction générique pour les occurrences de chaînes:
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;
}
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;
}
Nécessité de faire quelque chose de similaire pour tester les instructions conditionnelles à partir d'une chaîne.
Remplacé ce que je cherchais par un seul caractère et compté les occurrences du seul caractère.
Évidemment, le caractère unique que vous utilisez devra être vérifié pour ne pas exister dans la chaîne avant que cela ne se produise pour éviter des décomptes incorrects.
string s = "HOWLYH THIS ACTUALLY WORKSH WOWH";
int count = 0;
for (int i = 0; i < s.Length; i++)
if (s[i] == 'H') count++;
Il vérifie simplement chaque caractère de la chaîne. Si le caractère est celui que vous recherchez, ajoutez-en un à compter.
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;
C'est pour compter l'occurrence du personnage. Pour cet exemple, la sortie sera "a4b4j3"
Ma prise initiale m'a donné quelque chose comme:
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;
}
L'aiguille dans une approche de botte de foin utilisant replace et division rapporte 21 secondes ou plus, alors que cela prend environ 15,2 secondes.
Editez après avoir ajouté un bit qui ajouterait substring.Length - 1
au charIndex (comme il se doit), il est à 11.6 secondes.
Edit 2: J'ai utilisé une chaîne qui avait 26 chaînes de deux caractères, voici les temps mis à jour avec les mêmes exemples de textes:
Aiguille dans une botte de foin (version OP): 7.8 secondes
Mécanisme suggéré: 4,6 secondes.
Éditer 3: Ajout du coin cas unique, il est passé à 1.2 secondes.
Edit 4: Pour le contexte: 50 millions d'itérations ont été utilisées.
Pour le cas d'un délimiteur de chaîne (pas pour le caractère cas, comme le dit le sujet):
string source = "@@@ once @@@ on @@@ a @@@ time @@@";
int count = source.Split (new [] {"@@@"}, StringSplitOptions.RemoveEmptyEntries) .Length - 1;
Le délimiteur naturel de la valeur source originale de l'affiche ("/ une fois sur/une/une/heure /") est un caractère '/' et les réponses expliquent l'option source.Split (char []) bien que ...
Je pensais lancer ma méthode d’extension dans le ring (voir les commentaires pour plus d’informations). Je n'ai pas procédé à des analyses formelles, mais je pense que cela doit être très rapide pour la plupart des scénarios.
EDIT: OK - alors cette SO question m’a amené à me demander comment les performances de notre implémentation actuelle se situeraient par rapport à certaines des solutions présentées ici. J'ai décidé de faire un peu de comparaison et de constater que notre solution correspondait tout à fait aux performances de la solution fournie par Richard Watson jusqu'à ce que vous effectuiez une recherche agressive avec des chaînes de grande taille (100 Kb +), grandes sous-chaînes (32 Kb +) et de nombreuses répétitions intégrées (10K +). À ce stade, notre solution était environ 2 à 4 fois plus lente. Compte tenu de cela et du fait que nous aimons beaucoup la solution présentée par Richard Watson, nous avons restructuré notre solution en conséquence. Je voulais juste rendre ceci disponible pour quiconque pourrait en bénéficier.
Notre solution originale:
/// <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;
}
Et voici notre solution révisée:
/// <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;
}
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");
Si vous consultez cette page Web , vous disposez de 15 méthodes différentes pour effectuer cette analyse, y compris l’utilisation de boucles parallèles.
Le moyen le plus rapide semble utiliser une seule boucle for threadée (si vous avez la version .Net <4.0) ou une boucle parallel.for (si vous utilisez .Net> 4.0 avec des milliers de vérifications).
En supposant que "ss" est votre chaîne de recherche, "ch" est votre tableau de caractères (si vous avez plus d'un caractère que vous cherchez), voici le résumé de base du code qui a eu le temps d'exécution le plus rapide avec un seul thread:
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.
}
}
}
Le code source de référence est également fourni afin que vous puissiez exécuter vos propres tests.
using System.Linq;
int CountOf => "A :: B C :: D" .Split ("::"). Longueur - 1;