Trouver la nième occurrence d’un caractère dans une chaîne

J’ai besoin d’aide pour créer une méthode C # qui renvoie l’index de l’occurrence Nth d’un caractère dans une chaîne.

Par exemple, la 3ème occurrence du caractère 't' dans la chaîne "dtststxtu" est 5.
(Notez que la chaîne a 4 t s.)

 public int GetNthIndex(ssortingng s, char t, int n) { int count = 0; for (int i = 0; i < s.Length; i++) { if (s[i] == t) { count++; if (count == n) { return i; } } } return -1; } 

Cela pourrait être beaucoup plus propre, et il n'y a pas de contrôle sur les entrées.

Il y a un bug mineur dans la solution précédente.

Voici un code mis à jour:

 s.TakeWhile(c => (n -= (c == t ? 1 : 0)) > 0).Count(); 

Mise à jour: Index de la Nth Occurance One-Liner:

 int NthOccurence(ssortingng s, char t, int n) { s.TakeWhile(c => n - (c == t)?1:0 > 0).Count(); } 

Utilisez-les à vos risques et périls. Cela ressemble à des devoirs, alors j’ai laissé quelques bugs pour que vous trouviez:

 int CountChars(ssortingng s, char t) { int count = 0; foreach (char c in s) if (s.Equals(t)) count ++; return count; } 

.

 int CountChars(ssortingng s, char t) { return s.Length - s.Replace(t.ToSsortingng(), "").Length; } 

.

 int CountChars(ssortingng s, char t) { Regex r = new Regex("[\\" + t + "]"); return r.Match(s).Count; } 

Voici une implémentation récursive – en tant que méthode d’extension, mimant le format de la ou des méthodes du framework:

 public static int IndexOfNth( this ssortingng input, ssortingng value, int startIndex, int nth) { if (nth < 1) throw new NotSupportedException("Param 'nth' must be greater than 0!"); if (nth == 1) return input.IndexOf(value, startIndex); return input.IndexOfNth(value, input.IndexOf(value, startIndex) + 1, --nth); } 

En outre, voici quelques tests unitaires (MBUnit) qui pourraient vous aider (pour prouver que c'est correct):

 [Test] public void TestIndexOfNthWorksForNth1() { const ssortingng input = "foo
bar
baz
"; Assert.AreEqual(3, input.IndexOfNth("
", 0, 1)); } [Test] public void TestIndexOfNthWorksForNth2() { const ssortingng input = "foo
whatthedeuce
kthxbai
"; Assert.AreEqual(21, input.IndexOfNth("
", 0, 2)); } [Test] public void TestIndexOfNthWorksForNth3() { const ssortingng input = "foo
whatthedeuce
kthxbai
"; Assert.AreEqual(34, input.IndexOfNth("
", 0, 3)); }

Voici une autre solution LINQ:

 ssortingng input = "dtststx"; char searchChar = 't'; int occurrencePosition = 3; // third occurrence of the char var result = input.Select((c, i) => new { Char = c, Index = i }) .Where(item => item.Char == searchChar) .Skip(occurrencePosition - 1) .FirstOrDefault(); if (result != null) { Console.WriteLine("Position {0} of '{1}' occurs at index: {2}", occurrencePosition, searchChar, result.Index); } else { Console.WriteLine("Position {0} of '{1}' not found!", occurrencePosition, searchChar); } 

Juste pour le plaisir, voici une solution Regex. J’ai vu certaines personnes utiliser initialement Regex pour compter, mais lorsque la question a changé, aucune mise à jour n’a été effectuée. Voici comment cela peut être fait avec Regex – encore une fois, juste pour le plaisir. L’approche traditionnelle est la meilleure pour la simplicité.

 ssortingng input = "dtststx"; char searchChar = 't'; int occurrencePosition = 3; // third occurrence of the char Match match = Regex.Matches(input, Regex.Escape(searchChar.ToSsortingng())) .Cast() .Skip(occurrencePosition - 1) .FirstOrDefault(); if (match != null) Console.WriteLine("Index: " + match.Index); else Console.WriteLine("Match not found!"); 

La réponse de Joel est bonne (et je l’ai votée). Voici une solution basée sur LINQ:

 yourSsortingng.Where(c => c == 't').Count(); 

ranomore a correctement commenté que le one-liner de Joel Coehoorn ne fonctionne pas.

Voici une méthode à deux lignes qui fonctionne, une méthode d’extension de chaîne qui renvoie l’index basé sur 0 de la nième occurrence d’un caractère, ou -1 si aucune occurrence n’existe:

 public static class SsortingngExtensions { public static int NthIndexOf(this ssortingng s, char c, int n) { var takeCount = s.TakeWhile(x => (n -= (x == c ? 1 : 0)) > 0).Count(); return takeCount == s.Length ? -1 : takeCount; } } 

Voici une façon amusante de le faire

  int i = 0; ssortingng s="asdasdasd"; int n = 3; s.Where(b => (b == 'd') && (i++ == n)); return i; 
 ssortingng result = "i am '[email protected]'"; // ssortingng int in1 = result.IndexOf('\''); // get the index of first quote int in2 = result.IndexOf('\'', in1 + 1); // get the index of second ssortingng quoted_text = result.Subssortingng(in1 + 1, in2 - in1); // get the ssortingng between quotes 

J’ajoute une autre réponse assez rapide par rapport aux autres méthodes

 private static int IndexOfNth(ssortingng str, char c, int nth, int startPosition = 0) { int index = str.IndexOf(c, startPosition); if (index >= 0 && nth > 1) { return IndexOfNth(str, c, nth - 1, index + 1); } return index; } 

vous pouvez faire ce travail avec les expressions régulières.

  ssortingng input = "dtststx"; char searching_char = 't'; int output = Regex.Matches(input, "["+ searching_char +"]")[2].Index; 

Sincères salutations.

 public int GetNthOccurrenceOfChar(ssortingng s, char c, int occ) { return Ssortingng.Join(c.ToSsortingng(), s.Split(new char[] { c }, SsortingngSplitOptions.None).Take(occ)).Length; } 

Si vous êtes intéressé, vous pouvez également créer des méthodes d’extension de chaîne comme ceci:

  public static int Search(this ssortingng yourSsortingng, ssortingng yourMarker, int yourInst = 1, bool caseSensitive = true) { //returns the placement of a ssortingng in another ssortingng int num = 0; int currentInst = 0; //if optional argument, case sensitive is false convert ssortingng and marker to lowercase if (!caseSensitive) { yourSsortingng = yourSsortingng.ToLower(); yourMarker = yourMarker.ToLower(); } int myReturnValue = -1; //if nothing is found the returned integer is negative 1 while ((num + yourMarker.Length) <= yourString.Length) { string testString = yourString.Substring(num, yourMarker.Length); if (testString == yourMarker) { currentInst++; if (currentInst == yourInst) { myReturnValue = num; break; } } num++; } return myReturnValue; } public static int Search(this string yourString, char yourMarker, int yourInst = 1, bool caseSensitive = true) { //returns the placement of a string in another string int num = 0; int currentInst = 0; var charArray = yourString.ToArray(); int myReturnValue = -1; if (!caseSensitive) { yourSsortingng = yourSsortingng.ToLower(); yourMarker = Char.ToLower(yourMarker); } while (num <= charArray.Length) { if (charArray[num] == yourMarker) { currentInst++; if (currentInst == yourInst) { myReturnValue = num; break; } } num++; } return myReturnValue; } 

Une autre solution basée sur RegEx (non testée):

 int NthIndexOf(ssortingng s, char t, int n) { if(n < 0) { throw new ArgumentException(); } if(n==1) { return s.IndexOf(t); } if(t=="") { return 0; } string et = RegEx.Escape(t); string pat = "(?<=" + Microsoft.VisualBasic.StrDup(n-1, et + @"[.\n]*") + ")" + et; Match m = RegEx.Match(s, pat); return m.Success ? m.Index : -1; } 

Cela devrait être légèrement plus optimal que de demander à RegEx de créer une collection de correspondances, uniquement pour éliminer toutes les correspondances sauf une.

  public static int FindOccuranceOf(this ssortingng str,char @char, int occurance) { var result = str.Select((x, y) => new { Letter = x, Index = y }) .Where(letter => letter.Letter == @char).ToList(); if (occurence > result.Count || occurance <= 0) { throw new IndexOutOfRangeException("occurance"); } return result[occurance-1].Index ; } 

Salut à tous J’ai créé deux méthodes de surcharge pour trouver la nième occurrence de char et pour le texte avec moins de complexité sans naviguer à travers la boucle, ce qui augmente les performances de votre application.

 public static int NthIndexOf(ssortingng text, char searchChar, int nthindex) { int index = -1; try { var takeCount = text.TakeWhile(x => (nthindex -= (x == searchChar ? 1 : 0)) > 0).Count(); if (takeCount < text.Length) index = takeCount; } catch { } return index; } public static int NthIndexOf(string text, string searchText, int nthindex) { int index = -1; try { Match m = Regex.Match(text, "((" + searchText + ").*?){" + nthindex + "}"); if (m.Success) index = m.Groups[2].Captures[nthindex - 1].Index; } catch { } return index; } 

Étant donné que la fonction IndexOf intégrée est déjà optimisée pour rechercher un caractère dans une chaîne, une version encore plus rapide serait (en tant que méthode d’extension):

 public static int NthIndexOf(this ssortingng input, char value, int n) { if (n <= 0) throw new ArgumentOutOfRangeException("n", n, "n is less than zero."); int i = -1; do { i = input.IndexOf(value, i + 1); n--; } while (i != -1 && n > 0); return i; } 

Ou pour rechercher à partir de la fin de la chaîne en utilisant LastIndexOf :

 public static int NthLastIndexOf(this ssortingng input, char value, int n) { if (n <= 0) throw new ArgumentOutOfRangeException("n", n, "n is less than zero."); int i = input.Length; do { i = input.LastIndexOf(value, i - 1); n--; } while (i != -1 && n > 0); return i; } 

Rechercher une chaîne à la place d’un caractère est aussi simple que de modifier le type de paramètre de char en ssortingng et éventuellement append une surcharge pour spécifier la SsortingngComparison .

Marc Cals ‘LINQ Extended pour générique.

  using System; using System.Collections.Generic; using System.Linq; namespace fNns { public class indexer where T : IEquatable { public T t { get; set; } public int index { get; set; } } public static class fN { public static indexer findNth(IEnumerable tc, T t, int occurrencePosition) where T : IEquatable { var result = tc.Select((ti, i) => new indexer { t = ti, index = i }) .Where(item => item.t.Equals(t)) .Skip(occurrencePosition - 1) .FirstOrDefault(); return result; } public static indexer findNthReverse(IEnumerable tc, T t, int occurrencePosition) where T : IEquatable { var result = tc.Reverse().Select((ti, i) => new indexer {t = ti, index = i }) .Where(item => item.t.Equals(t)) .Skip(occurrencePosition - 1) .FirstOrDefault(); return result; } } } 

Quelques tests

  using System; using System.Collections.Generic; using NUnit.Framework; using Newtonsoft.Json; namespace FindNthNamespace.Tests { public class fNTests { [TestCase("pass", "dtststx", 't', 3, Result = "{\"t\":\"t\",\"index\":5}")] [TestCase("pass", new int[] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }, 0, 2, Result="{\"t\":0,\"index\":10}")] public ssortingng fNMethodTest(ssortingng scenario, IEnumerable tc, T t, int occurrencePosition) where T : IEquatable { Console.WriteLine(scenario); return JsonConvert.SerializeObject(fNns.fN.findNth(tc, t, occurrencePosition)).ToSsortingng(); } [TestCase("pass", "dtststxx", 't', 3, Result = "{\"t\":\"t\",\"index\":6}")] [TestCase("pass", new int[] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }, 0, 2, Result = "{\"t\":0,\"index\":19}")] public ssortingng fNMethodTestReverse(ssortingng scenario, IEnumerable tc, T t, int occurrencePosition) where T : IEquatable { Console.WriteLine(scenario); return JsonConvert.SerializeObject(fNns.fN.findNthReverse(tc, t, occurrencePosition)).ToSsortingng(); } } 

}

Voici une autre implémentation peut-être plus simple de la chaîne IndexOfNth() avec l’implémentation des chaînes.

Voici la version de correspondance de ssortingng :

 public static int IndexOfNth(this ssortingng source, ssortingng matchSsortingng, int charInstance, SsortingngComparison ssortingngComparison = SsortingngComparison.CurrentCulture) { if (ssortingng.IsNullOrEmpty(source)) return -1; int lastPos = 0; int count = 0; while (count < charInstance ) { var len = source.Length - lastPos; lastPos = source.IndexOf(matchString, lastPos,len,stringComparison); if (lastPos == -1) break; count++; if (count == charInstance) return lastPos; lastPos += matchString.Length; } return -1; } 

et la version de correspondance de char :

 public static int IndexOfNth(ssortingng source, char matchChar, int charInstance) { if (ssortingng.IsNullOrEmpty(source)) return -1; if (charInstance < 1) return -1; int count = 0; for (int i = 0; i < source.Length; i++) { if (source[i] == matchChar) { count++; if (count == charInstance) return i; } } return -1; } 

Je pense que pour une implémentation de bas niveau, vous devriez éviter d'utiliser LINQ, RegEx ou la récursivité pour réduire les frais généraux.