Encontrar todas as posições de substring em uma string maior em c #

Eu tenho uma string grande que eu preciso analisar, e eu preciso encontrar todas as instâncias de extract"(me,i-have lots. of]punctuation e armazenar o índice de cada um para uma lista.

Então digamos que este pedaço de corda estava no começo e no meio da corda maior, ambos seriam encontrados, e seus índices seriam adicionados à List . e a List conteria 0 e o outro índice, qualquer que fosse.

Eu tenho andado por aí, e a string.IndexOf faz quase o que eu estou procurando, e eu escrevi um código – mas não está funcionando e eu fui incapaz de descobrir exatamente o que está errado:

 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 = A lista
  • source = a corda grande

Alguma idéia melhor?

Aqui está um método de extensão de exemplo para isso:

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

Se você colocar isso em uma class estática e importar o namespace com o using , ele aparecerá como um método em qualquer string, e você pode apenas fazer:

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

Para obter mais informações sobre methods de extensão, http://msdn.microsoft.com/pt-br/library/bb383977.aspx

Também o mesmo usando um 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 que você não usa a class RegEx integrada:

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

Se você precisar reutilizar a expressão, compile-a e armazene-a em algum lugar. Altere o parâmetro matchString para um matchExpression de Regex em outra sobrecarga para o caso de reutilização.

usando o LINQ

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

Versão polida + case ignorando suporte:

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

Chame assim:

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

Olá boa resposta 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--; } } 

Mas isso abrange casos de testes como AOOAOOA onde substring

são AOOA e AOOA

Saída 0 e 3

Percebi que pelo menos duas soluções propostas não lidam com resultados de pesquisa sobrepostos. Eu não chequei o marcado com a marca de seleção verde. Aqui está um que lida com resultados de pesquisa sobrepostos:

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

Com base no código que usei para encontrar várias instâncias de uma string em uma string maior, seu código ficaria parecido com:

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

Sem o Regex, usando o tipo de comparação de string:

 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) 

Isso retorna {3,8,19,22}. O padrão vazio corresponderia a todas as posições.

Para vários padrões:

 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)) 

Isso retorna {3, 8, 19, 22, 15, 16}

O @csam está correto na teoria, embora seu código não seja compatível e possa ser refratificado para

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