Encontrar todas las posiciones de subcadena en una cadena más grande en C #

Tengo que analizar una cadena grande, y necesito encontrar todas las instancias de la extract"(me,i-have lots. of]punctuation de extract"(me,i-have lots. of]punctuation y almacenar el índice de cada una en una lista.

Por lo tanto, supongamos que este trozo de cuerda estaba al principio y al medio de la cuerda más grande, que se encontrarían ambos y que sus índices se agregarían a la List . y la List contendría 0 y el otro índice cualquiera que fuera.

He estado jugando, y el string.IndexOf hace casi lo que estoy buscando, y he escrito un código, pero no está funcionando y no he podido averiguar exactamente qué está mal:

 List inst = new List(); int index = 0; while (index < source.LastIndexOf("extract\"(me,i-have lots. of]punctuation", 0) + 39) { int src = source.IndexOf("extract\"(me,i-have lots. of]punctuation", index); inst.Add(src); index = src + 40; } 
  • inst = La lista
  • source = La cadena grande

Alguna mejor idea?

Aquí hay un método de extensión de ejemplo para ello:

 public static List AllIndexesOf(this string str, string value) { if (String.IsNullOrEmpty(value)) throw new ArgumentException("the string to find may not be empty", "value"); List indexes = new List(); for (int index = 0;; index += value.Length) { index = str.IndexOf(value, index); if (index == -1) return indexes; indexes.Add(index); } } 

Si coloca esto en una clase estática e importa el espacio de nombres con using , aparece como un método en cualquier cadena, y puede hacer lo siguiente:

 List indexes = "fooStringfooBar".AllIndexesOf("foo"); 

Para obtener más información sobre los métodos de extensión, http://msdn.microsoft.com/en-us/library/bb383977.aspx

También lo mismo usando un iterador:

 public static IEnumerable AllIndexesOf(this string str, string value) { if (String.IsNullOrEmpty(value)) throw new ArgumentException("the string to find may not be empty", "value"); for (int index = 0;; index += value.Length) { index = str.IndexOf(value, index); if (index == -1) break; yield return index; } } 

¿Por qué no usas la clase incorporada de RegEx?

 public static IEnumerable GetAllIndexes(this string source, string matchString) { matchString = Regex.Escape(matchString); foreach (Match match in Regex.Matches(source, matchString)) { yield return match.Index; } } 

Si necesita reutilizar la expresión, compílela y almacénela en algún lugar. Cambie el parámetro matchString a una expresión de coincidencia Regex en otra sobrecarga para el caso de reutilización.

usando LINQ

 public static IEnumerable IndexOfAll(this string sourceString, string subString) { return Regex.Matches(sourceString, subString).Cast().Select(m => m.Index); } 

Versión pulida + soporte ignorante de caja:

 public static int[] AllIndexesOf(string str, string substr, bool ignoreCase = false) { if (string.IsNullOrWhiteSpace(str) || string.IsNullOrWhiteSpace(substr)) { throw new ArgumentException("String or substring is not specified."); } var indexes = new List(); int index = 0; while ((index = str.IndexOf(substr, index, ignoreCase ? StringComparison.OrdinalIgnoreCase : StringComparison.Ordinal)) != -1) { indexes.Add(index++); } return indexes.ToArray(); } 
 public List GetPositions(string source, string searchString) { List ret = new List(); int len = searchString.Length; int start = -len; while (true) { start = source.IndexOf(searchString, start + len); if (start == -1) { break; } else { ret.Add(start); } } return ret; } 

Llámalo así:

 List list = GetPositions("bob is a chowder head bob bob sldfjl", "bob"); // list will contain 0, 22, 26 

Hola, buena respuesta por @Matti Virkkunen

 public static List AllIndexesOf(this string str, string value) { if (String.IsNullOrEmpty(value)) throw new ArgumentException("the string to find may not be empty", "value"); List indexes = new List(); for (int index = 0;; index += value.Length) { index = str.IndexOf(value, index); if (index == -1) return indexes; indexes.Add(index); index--; } } 

Pero esto cubre casos de prueba como AOOAOOA donde subcadena

son AOOA y AOOA

Salida 0 y 3

Noté que al menos dos soluciones propuestas no manejan coincidencias de búsqueda superpuestas. No marqué el marcado con la marca de verificación verde. Aquí hay uno que maneja los hits de búsqueda superpuestos:

  public static List GetPositions(this string source, string searchString) { List ret = new List(); int len = searchString.Length; int start = -1; while (true) { start = source.IndexOf(searchString, start +1); if (start == -1) { break; } else { ret.Add(start); } } return ret; } 

Basado en el código que he usado para encontrar varias instancias de una cadena dentro de una cadena más grande, su código se vería así:

 List inst = new List(); int index = 0; while (index >=0) { index = source.IndexOf("extract\"(me,i-have lots. of]punctuation", index); inst.Add(index); index++; } 
 public static Dictionary> GetWordsPositions(this string input, string[] Susbtrings) { Dictionary> WordsPositions = new Dictionary>(); IEnumerable IndexOfAll = null; foreach (string st in Susbtrings) { IndexOfAll = Regex.Matches(input, st).Cast().Select(m => m.Index); WordsPositions.Add(st, IndexOfAll); } return WordsPositions; } 

Sin Regex, usando el tipo de comparación de cadenas:

 string search = "123aa456AA789bb9991AACAA"; string pattern = "AA"; Enumerable.Range(0, search.Length) .Select(index => { return new { Index = index, Length = (index + pattern.Length) > search.Length ? search.Length - index : pattern.Length }; }) .Where(searchbit => searchbit.Length == pattern.Length && pattern.Equals(search.Substring(searchbit.Index, searchbit.Length),StringComparison.OrdinalIgnoreCase)) .Select(searchbit => searchbit.Index) 

Esto devuelve {3,8,19,22}. El patrón vacío coincidiría con todas las posiciones.

Para patrones múltiples:

 string search = "123aa456AA789bb9991AACAA"; string[] patterns = new string[] { "aa", "99" }; patterns.SelectMany(pattern => Enumerable.Range(0, search.Length) .Select(index => { return new { Index = index, Length = (index + pattern.Length) > search.Length ? search.Length - index : pattern.Length }; }) .Where(searchbit => searchbit.Length == pattern.Length && pattern.Equals(search.Substring(searchbit.Index, searchbit.Length), StringComparison.OrdinalIgnoreCase)) .Select(searchbit => searchbit.Index)) 

Esto devuelve {3, 8, 19, 22, 15, 16}

@csam es correcto en teoría, aunque su código no cumplirá y puede ser refractado a

 public static IEnumerable IndexOfAll(this string sourceString, string matchString) { matchString = Regex.Escape(matchString); return from Match match in Regex.Matches(sourceString, matchString) select match.Index; }