Obtener el índice de la enésima aparición de una cadena?

A menos que me falta un método incorporado obvio, ¿cuál es la forma más rápida de obtener la n- ésima ocurrencia de una cadena dentro de una cadena?

Me doy cuenta de que podría recorrer el método IndexOf actualizando su índice de inicio en cada iteración del ciclo. Pero hacerlo de esta manera me parece un desperdicio.

Eso es básicamente lo que necesita hacer, o al menos, es la solución más fácil. Lo único que “malgastaría” es el costo de n invocaciones de métodos: en realidad no revisará ningún caso dos veces, si lo piensa. (IndexOf volverá tan pronto como encuentre la coincidencia, y continuará desde donde quedó).

Realmente podría usar la expresión regular /((s).*?){n}/ para buscar n-ésima aparición de subcadena s .

En C # podría verse así:

 public static class StringExtender { public static int NthIndexOf(this string target, string value, int n) { Match m = Regex.Match(target, "((" + Regex.Escape(value) + ").*?){" + n + "}"); if (m.Success) return m.Groups[2].Captures[n - 1].Index; else return -1; } } 

Nota: He agregado Regex.Escape a la solución original para permitir buscar caracteres que tengan un significado especial para regex engine.

Eso es básicamente lo que necesita hacer, o al menos, es la solución más fácil. Lo único que “malgastaría” es el costo de n invocaciones de métodos: en realidad no revisará ningún caso dos veces, si lo piensa. (IndexOf volverá tan pronto como encuentre la coincidencia, y continuará desde donde quedó).

Aquí está la implementación recursiva (de la idea anterior) como un método de extensión, imitando el formato de los métodos del marco:

 public static int IndexOfNth(this string input, string 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); var idx = input.IndexOf(value, startIndex); if (idx == -1) return -1; return input.IndexOfNth(value, idx + 1, --nth); } 

Además, aquí hay algunas pruebas de unidades (MBUnit) que pueden ayudarlo (para demostrar que es correcto):

 using System; using MbUnit.Framework; namespace IndexOfNthTest { [TestFixture] public class Tests { //has 4 instances of the private const string Input = "TestTest"; private const string Token = "Test"; /* Test for 0th index */ [Test] public void TestZero() { Assert.Throws( () => Input.IndexOfNth(Token, 0, 0)); } /* Test the two standard cases (1st and 2nd) */ [Test] public void TestFirst() { Assert.AreEqual(0, Input.IndexOfNth("Test", 0, 1)); } [Test] public void TestSecond() { Assert.AreEqual(4, Input.IndexOfNth("Test", 0, 2)); } /* Test the 'out of bounds' case */ [Test] public void TestThird() { Assert.AreEqual(-1, Input.IndexOfNth("Test", 0, 3)); } /* Test the offset case (in and out of bounds) */ [Test] public void TestFirstWithOneOffset() { Assert.AreEqual(4, Input.IndexOfNth("Test", 4, 1)); } [Test] public void TestFirstWithTwoOffsets() { Assert.AreEqual(-1, Input.IndexOfNth("Test", 8, 1)); } } } 
 private int IndexOfOccurence(string s, string match, int occurence) { int i = 1; int index = 0; while (i <= occurence && (index = s.IndexOf(match, index + 1)) != -1) { if (i == occurence) return index; i++; } return -1; } 

o en C # con métodos de extensión

 public static int IndexOfOccurence(this string s, string match, int occurence) { int i = 1; int index = 0; while (i <= occurence && (index = s.IndexOf(match, index + 1)) != -1) { if (i == occurence) return index; i++; } return -1; } 

Tal vez también sería bueno trabajar con el String.Split() y verificar si la ocurrencia solicitada se encuentra en la matriz, si no necesita el índice, sino el valor en el índice

Después de algunas evaluaciones comparativas, esta parece ser la solución más simple y más eficiente

 public static int IndexOfNthSB(string input, char value, int startIndex, int nth) { if (nth < 1) throw new NotSupportedException("Param 'nth' must be greater than 0!"); var nResult = 0; for (int i = startIndex; i < input.Length; i++) { if (input[i] == value) nResult++; if (nResult == nth) return i; } return -1; } 

System.ValueTuple ftw:

var index = line.Select((x, i) => (x, i)).Where(x => x.Item1 == '"').ElementAt(5).Item2;

escribir una función de eso es tarea

Esto podría hacerlo:

 Console.WriteLine(str.IndexOf((@"\")+2)+1);