web-dev-qa-db-fra.com

Trouver toutes les positions de la sous-chaîne dans une chaîne plus grande en C #

J'ai une grande chaîne que je dois analyser, et je dois trouver toutes les instances de extract"(me,i-have lots. of]punctuation et stocker l'index de chacune dans une liste. 

Donc, supposons que ce morceau de ficelle se trouve au début et au milieu de la plus grande ficelle, les deux se trouvent, et leurs index sont ajoutés à la variable List et List contiendrait 0 et l’autre index quel qu’il soit.

J'ai joué, et le string.IndexOf fait presque ce que je cherche, et j'ai écrit du code - mais cela ne fonctionne pas et je suis incapable de comprendre exactement ce qui ne va pas:

List<int> inst = new List<int>();
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 liste
  • source = La grande chaîne

De meilleures idées?

55
caesay

Voici un exemple de méthode d'extension pour cela:

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

Si vous mettez cela dans une classe statique et importez l'espace de noms avec using, il apparaît comme une méthode sur n'importe quelle chaîne et vous pouvez simplement faire:

List<int> indexes = "fooStringfooBar".AllIndexesOf("foo");

Pour plus d'informations sur les méthodes d'extension, http://msdn.Microsoft.com/en-us/library/bb383977.aspx

Également la même chose en utilisant un itérateur:

public static IEnumerable<int> 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;
    }
}
103
Matti Virkkunen

Pourquoi n'utilisez-vous pas la classe RegEx intégrée:

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

Si vous avez besoin de réutiliser l'expression, compilez-la et mettez-la en cache quelque part. Remplacez le paramètre matchString par Regex matchExpression dans une autre surcharge pour le cas de réutilisation.

13
csaam

en utilisant LINQ 

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

Version polie + cas ignorant le support:

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>();
    int index = 0;

    while ((index = str.IndexOf(substr, index, ignoreCase ? StringComparison.OrdinalIgnoreCase : StringComparison.Ordinal)) != -1)
    {
        indexes.Add(index++);
    }

    return indexes.ToArray();
}
5
net_prog

Sans Regex, en utilisant le type de comparaison de chaîne:

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)

Ceci renvoie {3,8,19,22}. Le motif vide correspond à toutes les positions.

Pour plusieurs motifs:

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

Ceci renvoie {3, 8, 19, 22, 15, 16}

1
Sean

J'ai remarqué qu'au moins deux des solutions proposées ne traitent pas les résultats de recherche qui se chevauchent. Je n'ai pas coché celui marqué d'une coche verte. En voici un qui traite les résultats de recherche qui se chevauchent:

    public static List<int> GetPositions(this string source, string searchString)
    {
        List<int> ret = new List<int>();
        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;
    }
1
Kevin Baker

Salut belle réponse de @Matti Virkkunen

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

Mais cela couvre des cas de tests comme AOOAOOA Où sous-chaîne 

sont AOOA et AOOA

Sortie 0 et 3

1
Pranay Deep
public List<int> GetPositions(string source, string searchString)
{
    List<int> ret = new List<int>();
    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;
}

Appelez ça comme ça:

List<int> list = GetPositions("bob is a chowder head bob bob sldfjl", "bob");
// list will contain 0, 22, 26
1
MusiGenesis
public static Dictionary<string, IEnumerable<int>> GetWordsPositions(this string input, string[] Susbtrings)
{
    Dictionary<string, IEnumerable<int>> WordsPositions = new Dictionary<string, IEnumerable<int>>();
    IEnumerable<int> IndexOfAll = null;
    foreach (string st in Susbtrings)
    {
        IndexOfAll = Regex.Matches(input, st).Cast<Match>().Select(m => m.Index);
        WordsPositions.Add(st, IndexOfAll);

    }
    return WordsPositions;
}
0

@csam est correct en théorie, bien que son code ne compile pas et puisse être refactoré

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

Selon le code que j'ai utilisé pour rechercher plusieurs instances d'une chaîne dans une chaîne plus grande, votre code ressemblerait à ceci:

List<int> inst = new List<int>();
int index = 0;
while (index >=0)
{
    index = source.IndexOf("extract\"(me,i-have lots. of]punctuation", index);
    inst.Add(index);
    index++;
}
0
Corin