7

To search for a substring inside a string I can use the contains() function. But how can I check if a string contains a substring more than once?

To optimize that: For me it is sufficient to know that there is more than one result not how many.

vigri
  • 475
  • 1
  • 5
  • 16
  • I would loop through the string and remove everything from the first occurrence back, then update a counter and loop again. But there may be a better way to do this. – Melanie Feb 20 '13 at 16:37
  • Strongly related: [How to check if string contains single occurence of substring?](http://stackoverflow.com/questions/14775357). One answer suggests checking if `IndexOf` and `LastIndexOf` agree. – Jeppe Stig Nielsen Feb 20 '13 at 16:39

5 Answers5

19

Try to take advantage of fast IndexOf and LastIndexOf string methods. Use next code snippet. Idea is to check if first and last indexes are different and if first index is not -1, which means string is present.

string s = "tytyt";

var firstIndex = s.IndexOf("tyt");

var result = firstIndex != s.LastIndexOf("tyt") && firstIndex != -1;
Ilya Ivanov
  • 23,148
  • 4
  • 64
  • 90
  • I like this. Doesn't give you a count but does tell you that contains is plural. – Lloyd Feb 20 '13 at 16:37
  • @Lloyd if you want to search the `count` - it will be much slower algorithm. – Ilya Ivanov Feb 20 '13 at 16:39
  • Good example `"tytyt"` because it shows that overlapping of the two substring occurences is a possibility. Checking for two non-overlapping substring occurences is something else. – Jeppe Stig Nielsen Feb 20 '13 at 16:49
  • 1
    It is better to arrange `(a && b && ...)` in order of complexity, I believe `firstIndex != -1` first will make it short-circuit when false and some `LastIndexOf()` calls could potentially be saved. – ajax333221 Jun 16 '21 at 03:07
7

One line of code with RegEx:

return Regex.Matches(myString, "test").Count > 1;
Doug S
  • 10,146
  • 3
  • 40
  • 45
3

You can use following extension method which uses string.IndexOf:

public static bool ContainsMoreThan(this string text, int count, string value,  StringComparison comparison)
{
    if (text == null) throw new ArgumentNullException("text");
    if (string.IsNullOrEmpty(value))
        return text != "";

    int contains = 0;
    int index = 0;

    while ((index = text.IndexOf(value, index, text.Length - index, comparison)) != -1)
    {
        if (++contains > count)
            return true;
        index++;
    }
    return false;
}

Use it in the following way:

string text = "Lorem ipsum dolor sit amet, quo porro homero dolorem eu, facilisi inciderint ius in.";
bool containsMoreThanOnce = text.ContainsMoreThan(1, "dolor", StringComparison.OrdinalIgnoreCase); // true

Demo

It's a string extension and enables to pass the count, the value you search and the StringComparison(for example to search case-insensitively).

Tim Schmelter
  • 450,073
  • 74
  • 686
  • 939
  • You may need to use `index + 1` to avoid finding the same substring occurence twice. – Jeppe Stig Nielsen Feb 20 '13 at 16:44
  • @JeppeStigNielsen: But i'm using `index + 1` already (ok, my first version did not) – Tim Schmelter Feb 20 '13 at 16:54
  • Yeah, didn't see your edit when I commented. Maybe if `toSearch` had only `Length` one, and the first (and only) occurence of it were at the very end of `text`, then your last call to `IndexOf` would throw an exception. – Jeppe Stig Nielsen Feb 20 '13 at 18:21
  • @JeppeStigNielsen: No, it was exception free imho. However, now i've made it an extension and improved it in several ways. – Tim Schmelter Feb 21 '13 at 14:57
  • 1
    It's cool now (upvoted). I thought if you searched for a length-1 string and it was found in the very end, it would fail, but I tried it out, and it only fails with the empty string. With your new code, the call `"Hello".ContainsMoreThan(100, "", StringComparison.Ordinal);` throws a "bad" exception. Yes, it's pretty meaningles to search for the empty string, of course, but it could happen if you didn't know that the string was empty. – Jeppe Stig Nielsen Feb 21 '13 at 16:43
  • @JeppeStigNielsen: I've added an `if (string.IsNullOrEmpty(value))` check which should prevent it. Are you working in the quality management department? ;-) – Tim Schmelter Feb 21 '13 at 16:50
  • Nah, not really. I saw some other code where they added `value.Length` to `index` (like `index += value.Length`) instead of just incrementing `index` (they didn't want overlapping substrings), so in that case they could get past the right end of the string `text`. Now, in your code above, if you change your `break;` into an immediate `return false;`, it may be a little more beautiful because then the `return true;` can be moved to outside of the loop. I think. – Jeppe Stig Nielsen Feb 21 '13 at 16:57
  • @JeppeStigNielsen: I think i've found the most concise version, now even the while-expression is useful ;) – Tim Schmelter Feb 21 '13 at 21:04
3

You could also use the Regex class. msdn regex

   int count;
   Regex regex = new Regex("your search pattern", RegexOptions.IgnoreCase);
   MatchCollection matches = regex.Matches("your string");
   count = matches.Count;
faceman
  • 1,318
  • 11
  • 20
2
private bool MoreThanOnce(string full, string part)
{
   var first = full.IndexOf(part);
   return first!=-1 && first != full.LastIndexOf(part);
}
YuriyP
  • 4,210
  • 4
  • 25
  • 35