142

I want to check if a String s, contains "a" or "b" or "c", in C#. I am looking for a nicer solution than using

if (s.contains("a")||s.contains("b")||s.contains("c"))
one noa
  • 345
  • 1
  • 3
  • 10
Stavros
  • 5,802
  • 13
  • 32
  • 45

15 Answers15

115

Well, there's always this:

public static bool ContainsAny(this string haystack, params string[] needles)
{
    foreach (string needle in needles)
    {
        if (haystack.Contains(needle))
            return true;
    }

    return false;
}

Usage:

bool anyLuck = s.ContainsAny("a", "b", "c");

Nothing's going to match the performance of your chain of || comparisons, however.

Dan Tao
  • 125,917
  • 54
  • 300
  • 447
  • 18
    Adding new short syntax to this nice solution `public static bool ContainsAny(this string haystack, params string[] needles) { return needles.Any(haystack.Contains); }` – simonkaspers1 May 05 '17 at 08:17
  • Simple and obvious solution. But is there any good ready to use implementation that not require multiple iterations through haystack string? I can implement it by myself, iterating through haystack string characters and comparing first characters of needles sequentially in one go, but I can't believe that such trivial solution not implemented yet in some well-known NuGet library. – RollerKostr Apr 20 '18 at 07:23
  • 2
    @RollerKostr It's not built into C# (yet) so why add additional dependencies in your project for such a simple solution? – jmdon Apr 26 '18 at 12:26
  • This is the way. Bear in mind about cases! You people might want to apply ToUpperInvariant() on both "sides" – Gonzo345 Aug 20 '21 at 09:03
114

Here's a LINQ solution which is virtually the same but more scalable:

new[] { "a", "b", "c" }.Any(c => s.Contains(c))
Jeff Mercado
  • 129,526
  • 32
  • 251
  • 272
  • 9
    That is scalable in the sense that it's easy to add characters, not in the sense of performance... :) – Guffa Aug 19 '10 at 07:31
  • 4
    Ah yes, of course. Perhaps "more extensible" would have been a better choice of words. – Jeff Mercado Aug 19 '10 at 07:39
  • 3
    The performance won't be terrible. Better than an interpreted regexp, anyhow. – Steven Sudit Aug 19 '10 at 07:53
  • Awesome answer just for completeness you can split your incoming string up into an array first e.g: var splitStringArray = someString.Split(' '); Then you can do something like: if(someStringArray.Any(s => otherString.Contains(s))) { // do something } Hope that helps someone for clarity. – Trevor Oct 20 '16 at 04:39
  • it's cool trick. tnx. – Unknown Artist Jun 24 '22 at 10:30
82
var values = new [] {"abc", "def", "ghj"};
var str = "abcedasdkljre";
values.Any(str.Contains);
user3067967
  • 821
  • 6
  • 2
51

If you are looking for single characters, you can use String.IndexOfAny().

If you want arbitrary strings, then I'm not aware of a .NET method to achieve that "directly", although a regular expression would work.

Jason Williams
  • 56,972
  • 11
  • 108
  • 137
24

You can try with regular expression

string s;
Regex r = new Regex ("a|b|c");
bool containsAny = r.IsMatch (s);
jethro
  • 18,177
  • 7
  • 46
  • 42
22

If you need ContainsAny with a specific StringComparison (for example to ignore case) then you can use this String Extentions method.

public static class StringExtensions
{
    public static bool ContainsAny(this string input, IEnumerable<string> containsKeywords, StringComparison comparisonType)
    {
        return containsKeywords.Any(keyword => input.IndexOf(keyword, comparisonType) >= 0);
    }
}

Usage with StringComparison.CurrentCultureIgnoreCase:

var input = "My STRING contains Many Substrings";
var substrings = new[] {"string", "many substrings", "not containing this string" };
input.ContainsAny(substrings, StringComparison.CurrentCultureIgnoreCase);
// The statement above returns true.

”xyz”.ContainsAny(substrings, StringComparison.CurrentCultureIgnoreCase);
// This statement returns false.
Mikael Engver
  • 4,634
  • 4
  • 46
  • 53
  • 3
    Just one note to improve this answer. You could write it even more ellegant with params keyword : ContainsAny(this string input, StringComparison comparisonType, params string [] containsKeywords) and use like input.ContainsAny(substrings, StringComparison.CurrentCultureIgnoreCase, "string", "many substrings"...etc) – Roma Borodov Jan 08 '17 at 16:20
9

This is a "nicer solution" and quite simple

if(new string[] { "A", "B", ... }.Any(s=>myString.Contains(s)))
slavoo
  • 5,798
  • 64
  • 37
  • 39
Simi2525
  • 231
  • 3
  • 2
7
List<string> includedWords = new List<string>() { "a", "b", "c" };
bool string_contains_words = includedWords.Exists(o => s.Contains(o));
Amjad Abu Saa
  • 1,644
  • 2
  • 15
  • 11
6
public static bool ContainsAny(this string haystack, IEnumerable<string> needles)
{
    return needles.Any(haystack.Contains);
}
Simon
  • 33,714
  • 21
  • 133
  • 202
3

As a string is a collection of characters, you can use LINQ extension methods on them:

if (s.Any(c => c == 'a' || c == 'b' || c == 'c')) ...

This will scan the string once and stop at the first occurance, instead of scanning the string once for each character until a match is found.

This can also be used for any expression you like, for example checking for a range of characters:

if (s.Any(c => c >= 'a' && c <= 'c')) ...
Guffa
  • 687,336
  • 108
  • 737
  • 1,005
  • Agreed. This solves the problem of multiple scans when the first conditions don't match. Wonder what the overhead of the lambda is though? Shouldn't be much once though. – bruceboughton Aug 19 '10 at 08:57
2
// Nice method's name, @Dan Tao

public static bool ContainsAny(this string value, params string[] params)
{
    return params.Any(p => value.Compare(p) > 0);
    // or
    return params.Any(p => value.Contains(p));
}

Any for any, All for every

abatishchev
  • 98,240
  • 88
  • 296
  • 433
2
    static void Main(string[] args)
    {
        string illegalCharacters = "!@#$%^&*()\\/{}|<>,.~`?"; //We'll call these the bad guys
        string goodUserName = "John Wesson";                   //This is a good guy. We know it. We can see it!
                                                               //But what if we want the program to make sure?
        string badUserName = "*_Wesson*_John!?";                //We can see this has one of the bad guys. Underscores not restricted.

        Console.WriteLine("goodUserName " + goodUserName +
            (!HasWantedCharacters(goodUserName, illegalCharacters) ?
            " contains no illegal characters and is valid" :      //This line is the expected result
            " contains one or more illegal characters and is invalid"));
        string captured = "";
        Console.WriteLine("badUserName " + badUserName +
            (!HasWantedCharacters(badUserName, illegalCharacters, out captured) ?
            " contains no illegal characters and is valid" :
            //We can expect this line to print and show us the bad ones
            " is invalid and contains the following illegal characters: " + captured));  

    }

    //Takes a string to check for the presence of one or more of the wanted characters within a string
    //As soon as one of the wanted characters is encountered, return true
    //This is useful if a character is required, but NOT if a specific frequency is needed
    //ie. you wouldn't use this to validate an email address
    //but could use it to make sure a username is only alphanumeric
    static bool HasWantedCharacters(string source, string wantedCharacters)
    {
        foreach(char s in source) //One by one, loop through the characters in source
        {
            foreach(char c in wantedCharacters) //One by one, loop through the wanted characters
            {
                if (c == s)  //Is the current illegalChar here in the string?
                    return true;
            }
        }
        return false;
    }

    //Overloaded version of HasWantedCharacters
    //Checks to see if any one of the wantedCharacters is contained within the source string
    //string source ~ String to test
    //string wantedCharacters ~ string of characters to check for
    static bool HasWantedCharacters(string source, string wantedCharacters, out string capturedCharacters)
    {
        capturedCharacters = ""; //Haven't found any wanted characters yet

        foreach(char s in source)
        {
            foreach(char c in wantedCharacters) //Is the current illegalChar here in the string?
            {
                if(c == s)
                {
                    if(!capturedCharacters.Contains(c.ToString()))
                        capturedCharacters += c.ToString();  //Send these characters to whoever's asking
                }
            }
        }

        if (capturedCharacters.Length > 0)  
            return true;
        else
            return false;
    }
  • 1
    The methods HasWantedCharacters accept two or three strings. The first string we want to check for certain characters. The second string, all the characters that we'll look for in the first. The overloaded method provides output to the caller (ie Main) as a third string. A nested foreach statement goes through each character in the source and compares it, one-by-one; with those characters we're checking for. If one of the characters is found, it returns true. The overloaded method outputs a string of characters found matching those checked for, but won't return until all are out. Helpful? – Nate Wilkins Sep 23 '17 at 21:54
  • 1
    Feel free to start a C# console project and copy the code inside the program class-be sure to replace the main method. Tinker with the two strings (goodUserName and badUserName) and you might see just what the methods do and how they work. The examples are longer so as to provide a workable solution that can be modified without delimiters like commas. Escape sequences are just one way to represent the single quote and backslash if you need to check for them. – Nate Wilkins Sep 23 '17 at 22:04
1

You could have a class for your extension methods and add this one:

    public static bool Contains<T>(this string s, List<T> list)
    {
        foreach (char c in s)
        {
            foreach (T value in list)
            {
                if (c == Convert.ToChar(value))
                    return true;
            }
        }
        return false;
    }
0

You can use Regular Expressions

if(System.Text.RegularExpressions.IsMatch("a|b|c"))
David
  • 72,686
  • 18
  • 132
  • 173
0

If this is for a password checker with requirements, try this:

public static bool PasswordChecker(string input)
{
    // determins if a password is save enough
    if (input.Length < 8)
        return false;

    if (!new string[] { "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R",
                        "S", "T", "U", "V", "W", "X", "Y", "Z", "Ä", "Ü", "Ö"}.Any(s => input.Contains(s)))
    return false;

    if (!new string[] { "1", "2", "3", "4", "5", "6", "7", "8", "9", "0"}.Any(s => input.Contains(s)))
    return false;

    if (!new string[] { "!", "'", "§", "$", "%", "&", "/", "(", ")", "=", "?", "*", "#", "+", "-", "_", ".",
                        ",", ";", ":", "`", "´", "^", "°",   }.Any(s => input.Contains(s)))
    return false;

    return true; 
}

This will set a password to have a min length of 8, have it use at least one uppercase char, at least one number, and at least one of the special chars

innom
  • 770
  • 4
  • 19