Case insensitive 'Contains(string)'

Viewed 1111389

Is there a way to make the following return true?

string title = "ASTRINGTOTEST";
title.Contains("string");

There doesn't seem to be an overload that allows me to set the case sensitivity. Currently I UPPERCASE them both, but that's just silly (by which I am referring to the i18n issues that come with up- and down casing).

UPDATE

This question is ancient and since then I have realized I asked for a simple answer for a really vast and difficult topic if you care to investigate it fully.

For most cases, in mono-lingual, English code bases this answer will suffice. I'm suspecting because most people coming here fall in this category this is the most popular answer.

This answer however brings up the inherent problem that we can't compare text case insensitive until we know both texts are the same culture and we know what that culture is. This is maybe a less popular answer, but I think it is more correct and that's why I marked it as such.

29 Answers

You could use the String.IndexOf Method and pass StringComparison.OrdinalIgnoreCase as the type of search to use:

string title = "STRING";
bool contains = title.IndexOf("string", StringComparison.OrdinalIgnoreCase) >= 0;

Even better is defining a new extension method for string:

public static class StringExtensions
{
    public static bool Contains(this string source, string toCheck, StringComparison comp)
    {
        return source?.IndexOf(toCheck, comp) >= 0;
    }
}

Note, that null propagation ?. is available since C# 6.0 (VS 2015), for older versions use

if (source == null) return false;
return source.IndexOf(toCheck, comp) >= 0;

USAGE:

string title = "STRING";
bool contains = title.Contains("string", StringComparison.OrdinalIgnoreCase);

You can use IndexOf() like this:

string title = "STRING";

if (title.IndexOf("string", 0, StringComparison.OrdinalIgnoreCase) != -1)
{
    // The string exists in the original
}

Since 0 (zero) can be an index, you check against -1.

Microsoft .NET Documentation:

The zero-based index position of the value parameter from the start of the current instance if that string is found, or -1 if it is not. If value is Empty, the return value is startIndex.

.NET Core 2.0+ (including .NET 5.0+)

.NET Core has had a pair of methods to deal with this since version 2.0 :

  • String.Contains(Char, StringComparison)
  • String.Contains(String, StringComparison)

Example:

"Test".Contains("test", System.StringComparison.CurrentCultureIgnoreCase);

It is now officially part of the .NET Standard 2.1, and therefore part of all the implementations of the Base Class Library that implement this version of the standard (or a higher one).

You could always just up or downcase the strings first.

string title = "string":
title.ToUpper().Contains("STRING")  // returns true

Oops, just saw that last bit. A case insensitive compare would *probably* do the same anyway, and if performance is not an issue, I don't see a problem with creating uppercase copies and comparing those. I could have sworn that I once saw a case-insensitive compare once...

These are the easiest solutions.

  1. By Index of

    string title = "STRING";
    
    if (title.IndexOf("string", 0, StringComparison.CurrentCultureIgnoreCase) != -1)
    {
        // contains 
    }
    
  2. By Changing case

    string title = "STRING";
    
    bool contains = title.ToLower().Contains("string")
    
  3. By Regex

    Regex.IsMatch(title, "string", RegexOptions.IgnoreCase);
    

As simple and works

title.ToLower().Contains("String".ToLower())

You can use a string comparison parameter (available from .NET Core 2.1 and above) String.Contains Method.

public bool Contains (string value, StringComparison comparisonType);

Example:

string title = "ASTRINGTOTEST";
title.Contains("string", StringComparison.InvariantCultureIgnoreCase);

Just to build on the answer here, you can create a string extension method to make this a little more user-friendly:

    public static bool ContainsIgnoreCase(this string paragraph, string word)
    {
        return CultureInfo.CurrentCulture.CompareInfo.IndexOf(paragraph, word, CompareOptions.IgnoreCase) >= 0;
    }

if you want to check if your passed string is in string then there is a simple method for that.

string yourStringForCheck= "abc";
string stringInWhichWeCheck= "Test abc abc";

bool isContained = stringInWhichWeCheck.ToLower().IndexOf(yourStringForCheck.ToLower()) > -1;

This boolean value will return if the string is contained or not

Similar to previous answers (using an extension method) but with two simple null checks (C# 6.0 and above):

public static bool ContainsIgnoreCase(this string source, string substring)
{
    return source?.IndexOf(substring ?? "", StringComparison.OrdinalIgnoreCase) >= 0;
}

If source is null, return false (via null-propagation operator ?.)

If substring is null, treat as an empty string and return true (via null-coalescing operator ??)

The StringComparison can of course be sent as a parameter if needed.

The top-rated several answers are all good and correct in their own ways, I write here to add more information, context, and perspective.

For clarity, let us consider that string A contains string B if there is any subsequence of codepoints in A which is equal to B. If we accept this, the problem is reduced to the question of whether two strings are equal.

The question of when strings are equal has been considered in detail for many decades. Much of the present state of knowledge is encapsulated in SQL collations. Unicode normal forms are close to a proper subset of this. But there is more beyond even SQL collations.

For example, in SQL collations, you can be

  • Strictly binary sensitive - so that different Unicode normalisation forms (e.g. precombined or combining accents) compare differently.

    For example, é can be represented as either U+00e9 (precombined) or U+0065 U+0301 (e with combining acute accent).

    Are these the same or different?

  • Unicode normalised - In this case the above examples would be equal to each other, but not to É or e.

  • accent insensitive, (for e.g. Spanish, German, Swedish etc. text). In this case U+0065 = U+0065 U+0301 = U+00e9 = é = e

  • case and accent insensitive, so that (for e.g. Spanish, German, Swedish etc. text). In this case U+00e9 = U+0065 U+0301 = U+00c9 = U+0045 U+0301 = U+0049 = U+0065 = E = e = É = é

  • Kanatype sensitive or insensitive, i.e. you can consider Japanese Hiragana and Katakana as equivalent or different. The two syllabaries contain the same number of characters, organised and pronounced in the (mostly) the same way, but written differently and used for different purposes. For example katakana are used for loan words or foreign names, but hiragana are used for children's books, pronunciation guides (e.g. rubies), and where there is no kanji for a word (or perhaps where the writer does not know the kanji, or thinks the reader may not know it).

  • Full-width or half-width sensitive - Japanese encodings include two representations of some characters for historical reasons - they were displayed at different sizes.

  • Ligatures considered equivalent or not: See https://en.wikipedia.org/wiki/Ligature_(writing)

    Is æ the same as ae or not? They have different Unicode encodings, as do accented characters, but unlike accented characters they also look different.

    Which brings us to...

  • Arabic presentation form equivalence

    Arabic writing has a culture of beautiful calligraphy, where particular sequences of adjacent letters have specific representations. Many of these have been encoded in the Unicode standard. I don't fully understand the rules, but they seem to me to be analogous to ligatures.

  • Other scripts and systems: I have no knowledge whatsoever or Kannada, Malayalam, Sinhala, Thai, Gujarati, Tibetan, or almost all of the tens or hundreds of scripts not mentioned. I assume they have similar issues for the programmer, and given the number of issues mentioned so far and for so few scripts, they probably also have additional issues the programmer ought to consider.

That gets us out of the "encoding" weeds.

Now we must enter the "meaning" weeds.

  • is Beijing equal to 北京? If not, is Bĕijīng equal to 北京? If not, why not? It is the Pinyin romanisation.

  • Is Peking equal to 北京? If not, why not? It is the Wade-Giles romanisation.

  • Is Beijing equal to Peking? If not, why not?

Why are you doing this anyway?

For example, if you want to know if it is possible that two strings (A and B) refer to the same geographical location, or same person, you might want to ask:

  • Could these strings be either Wade-Giles or Pinyin representations of a set of sequences of Chinese characters? If so, is there any overlap between the corresponding sets?

  • Could one of these strings be a Cyrillic transcription of a Chinese Character?

  • could one of these strings be a Cyrillic transliteration of the Pinyin romanisation?

  • Could one of these strings be a Cyrillic transliteration of a Pinyin romanisation of a Sinification of an English name?

Clearly these are difficult questions, which don't have firm answers, and in any case, the answer may be different according to the purpose of the question.

To finish with a concrete example.

  • If you are delivering a letter or parcel, clearly Beijing, Peking, Bĕijīng and 北京 are all equal. For that purpose, they are all equally good. No doubt the Chinese post-offices recognise many other options, such as Pékin in French, Pequim in Portuguese, Bắc Kinh in Vietnamese, and Бээжин in Mongolian.

Words do not have fixed meanings.

Words are tools we use to navigate the world, to accomplish our tasks, and to communicate with other people.

While it looks like it would be helpful if words like equality, Beijing, or meaning had fixed meanings, the sad fact is they do not.

Yet we seem to muddle along somehow.

TL;DR: If you are dealing with questions relating to reality, in all its nebulosity (cloudiness, uncertainty, lack of clear boundaries), there are basically three possible answers to every question:

  • Probably
  • Probably not
  • Maybe
if ("strcmpstring1".IndexOf(Convert.ToString("strcmpstring2"), StringComparison.CurrentCultureIgnoreCase) >= 0){return true;}else{return false;}

Based on the existing answers and on the documentation of Contains method I would recommend the creation of the following extension which also takes care of the corner cases:

public static class VStringExtensions 
{
    public static bool Contains(this string source, string toCheck, StringComparison comp) 
    {
        if (toCheck == null) 
        {
            throw new ArgumentNullException(nameof(toCheck));
        }

        if (source.Equals(string.Empty)) 
        {
            return false;
        }

        if (toCheck.Equals(string.Empty)) 
        {
            return true;
        }

        return source.IndexOf(toCheck, comp) >= 0;
    }
}
Related