How to check if a String contains any of some strings
Asked Answered
W

16

147

With a String s, to determine if it contains "a" or "b" or "c". Something that is better than checking each individually:

if (s.contains("a")||s.contains("b")||s.contains("c"))
Whall answered 19/8, 2010 at 7:11 Comment(1)
For complex cases look up trie data structure.Pigweed
F
52

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.

Fayina answered 19/8, 2010 at 7:14 Comment(0)
C
123

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

new[] { "a", "b", "c" }.Any(c => s.Contains(c))
Crankle answered 19/8, 2010 at 7:25 Comment(5)
That is scalable in the sense that it's easy to add characters, not in the sense of performance... :)Waechter
Ah yes, of course. Perhaps "more extensible" would have been a better choice of words.Crankle
The performance won't be terrible. Better than an interpreted regexp, anyhow.Conley
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.Betthezel
it's cool trick. tnx.Urbas
C
116

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.

Catchweight answered 19/8, 2010 at 7:13 Comment(4)
Adding new short syntax to this nice solution public static bool ContainsAny(this string haystack, params string[] needles) { return needles.Any(haystack.Contains); }Prana
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.Finis
@Finis It's not built into C# (yet) so why add additional dependencies in your project for such a simple solution?Jessiajessica
This is the way. Bear in mind about cases! You people might want to apply ToUpperInvariant() on both "sides"Grisons
B
83
var values = new [] {"abc", "def", "ghj"};
var str = "abcedasdkljre";
values.Any(str.Contains);
Burdick answered 4/12, 2013 at 23:3 Comment(4)
This is elegant! Thanks!Cassiodorus
cool i didn't know that was possibleGranado
How can we ignore case insensitive with that way?Clute
This is elegant, but not intuitive? I like it implemented in the extension method above.Sunderance
F
52

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.

Fayina answered 19/8, 2010 at 7:14 Comment(0)
D
24

You can try with regular expression

string s;
Regex r = new Regex ("a|b|c");
bool containsAny = r.IsMatch (s);
Davison answered 19/8, 2010 at 7:14 Comment(5)
+1, though since he's looking for single characters a linq solution or indexOfAny might be more efficient.Jessamyn
+1 for the regular expression. that's what I would have gone for, if there wasn't IndexOfAnyWhall
Regular expressions are overkill for this.Conley
What makes people say regexes are overkill for this? If the regex is compiled once and used multiple times, and you have strings with only c in them or c near the beginning and a, b near the end, the regex would be far more efficient.Barm
Its not worked for special characters like - , ' " . ` =Bozcaada
A
24

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.
Allot answered 4/3, 2015 at 23:12 Comment(1)
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)Nonaggression
O
9

This is a "nicer solution" and quite simple

if(new string[] { "A", "B", ... }.Any(s=>myString.Contains(s)))
Orton answered 19/12, 2014 at 9:2 Comment(0)
D
8
List<string> includedWords = new List<string>() { "a", "b", "c" };
bool string_contains_words = includedWords.Exists(o => s.Contains(o));
Doubloon answered 27/8, 2017 at 4:21 Comment(0)
A
6
public static bool ContainsAny(this string haystack, IEnumerable<string> needles)
{
    return needles.Any(haystack.Contains);
}
Aromaticity answered 2/3, 2016 at 0:14 Comment(0)
W
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')) ...
Waechter answered 19/8, 2010 at 7:28 Comment(1)
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.Barm
H
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

Highstrung answered 19/8, 2010 at 7:41 Comment(0)
U
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;
    }
Unpin answered 23/9, 2017 at 20:36 Comment(2)
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?Unpin
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.Unpin
B
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;
    }
Bascio answered 29/7, 2021 at 16:26 Comment(0)
C
1

In .NET 8+, there is a new faster way for searching characters in strings. The new class is called SearchValues<T> and it allows you to do this:

using System.Buffers;

public class TestClass
{
    // A fixed set of characters. Works in .NET 8.
    private static readonly SearchValues<char> searchValues = SearchValues.Create([ 'a', 'b', 'c' ]);

    // A fixed set of strings. Works in .NET 9.
    // private static readonly SearchValues<string> searchValues2 = SearchValues.Create([ "Hi", "Hello" ], StringComparison.OrdinalIgnoreCase);

    static void Main(string[] args)
    {
        string text = "Hi friend";

        // In .NET 8.
        Console.WriteLine(text.AsSpan().ContainsAny(searchValues));

        // In .NET 9.
        // Console.WriteLine(text.AsSpan().ContainsAny(searchValues2));
    }
}

https://github.com/dotnet/runtime/pull/78093 shows the performance improvements one can get with SearchValues (note that it was originally called IndexOfAnyValues until it was renamed.)

Chose answered 15/2 at 10:24 Comment(1)
And when .NET9 is out, it will even support finding strings within a string.Yaws
M
0

You can use Regular Expressions

if(System.Text.RegularExpressions.IsMatch("a|b|c"))
Mcdevitt answered 19/8, 2010 at 7:14 Comment(1)
You certainly can, but I don't see why you would want to when almost anything else is better.Conley
S
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

Sundin answered 26/1, 2022 at 17:38 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.