Как проверить, содержит ли строка какие-либо строки


101

Я хочу проверить, содержит ли String s «a», «b» или «c» в C #. Я ищу лучшее решение, чем использование

if (s.contains("a")||s.contains("b")||s.contains("c"))

1
В сложных случаях ищите trieструктуру данных.
— Miserable Variable

Ответы:


45

Если вы ищете одиночные символы, вы можете использовать String.IndexOfAny().

Если вам нужны произвольные строки, то мне неизвестен метод .NET, позволяющий добиться этого «напрямую», хотя регулярное выражение могло бы работать.


97

Что ж, всегда такое:

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

    return false;
}

Применение:

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

Однако ничто не может сравниться с производительностью вашей цепочки ||сравнений.


14
Добавление нового короткого синтаксиса к этому красивому решению public static bool ContainsAny(this string haystack, params string[] needles) { return needles.Any(haystack.Contains); }
— simonkaspers1

Простое и очевидное решение. Но есть ли хорошая готовая реализация, которая не требует многократных итераций через строку стога сена? Я могу реализовать это самостоятельно, перебирая строковые символы стога сена и сравнивая первые символы иголок последовательно за один раз, но я не могу поверить, что такое тривиальное решение еще не реализовано в какой-то известной библиотеке NuGet.
— RollerKostr

@RollerKostr Он не встроен в C # (пока), так зачем добавлять дополнительные зависимости в ваш проект для такого простого решения?
— jmdon

74

Вот решение LINQ, которое практически такое же, но более масштабируемое:

new[] { "a", "b", "c" }.Any(c => s.Contains(c))

3
Это масштабируемо в том смысле, что легко добавлять персонажей, а не в смысле производительности ... :)
— Гуффа

2
Ах да, конечно. Возможно, лучшим выбором было бы слово «более расширяемый».
— Джефф Меркадо,

Спектакль не будет ужасным. Во всяком случае, лучше, чем интерпретируемое регулярное выражение.
— Стивен Судит,

Отличный ответ только для полноты, вы можете сначала разделить входящую строку на массив, например: var splitStringArray = someString.Split (''); Затем вы можете сделать что-то вроде: if (someStringArray.Any (s => otherString.Contains (s))) {// сделать что-нибудь} Надеюсь, это поможет кому-то для ясности.
— Тахир Халид

47
var values = new [] {"abc", "def", "ghj"};
var str = "abcedasdkljre";
values.Any(str.Contains);

Это элегантно! Благодарность!
— RK Coder

21

Вы можете попробовать с регулярным выражением

string s;
Regex r = new Regex ("a|b|c");
bool containsAny = r.IsMatch (s);

1
+1, хотя, поскольку он ищет отдельные символы, решение linq или indexOfAny может быть более эффективным.
— Джоэл Кохорн

+1 для регулярного выражения. это то, к чему я бы пошел, если бы не было IndexOfAny
— Ставрос

1
Для этого регулярные выражения - излишни.
— Стивен Судит,

3
Что заставляет людей говорить, что регулярные выражения излишни для этого? Если регулярное выражение скомпилировано один раз и используется несколько раз, и у вас есть строки, содержащие только c или c в начале и a, b в конце, регулярное выражение будет гораздо более эффективным.
— Брюсбоутон,

Не работает для специальных символов, таких как -, '"
— .`

15

Если вам нужен ContainsAny с конкретным StringComparison(например, чтобы игнорировать регистр), вы можете использовать этот метод String Extentions.

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

Использование с 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.

3
Всего одно примечание, чтобы улучшить этот ответ. Вы можете написать его еще более элегантно с ключевым словом params: ContainsAny (этот строковый ввод, StringComparison compareType, params string [] containsKeywords) и использовать как input.ContainsAny (подстроки, StringComparison.CurrentCultureIgnoreCase, "строка", "много подстрок" ... итд)
— Рома Бородов

8

Это "более приятное решение" и довольно простое

if(new string[] { "A", "B", ... }.Any(s=>myString.Contains(s)))

4

Поскольку строка представляет собой набор символов, вы можете использовать для них методы расширения LINQ:

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

Это будет сканировать строку один раз и останавливаться при первом возникновении, вместо того, чтобы сканировать строку один раз для каждого символа, пока не будет найдено совпадение.

Это также можно использовать для любого выражения, которое вам нравится, например, для проверки диапазона символов:

if (s.Any(c => c >= 'a' && c <= 'c')) ...

Согласовано. Это решает проблему множественных сканирований, когда первые условия не совпадают. Интересно, что такое накладные расходы на лямбда? Хотя не должно быть много раз.
— Брюсбоутон,

4
public static bool ContainsAny(this string haystack, IEnumerable<string> needles)
{
    return needles.Any(haystack.Contains);
}


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для любого, Allдля каждого


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
Методы HasWantedCharacters принимают две или три строки. Первая строка, которую мы хотим проверить на наличие определенных символов. Вторая строка, все символы, которые мы будем искать в первой. Перегруженный метод обеспечивает вывод вызывающей стороне (т. Е. Main) в виде третьей строки. Вложенный оператор foreach просматривает каждый символ в источнике и сравнивает его один за другим; с теми персонажами, которых мы ищем. Если один из символов найден, возвращается истина. Перегруженный метод выводит строку символов, найденных, совпадающих с проверенными, но не вернется, пока все не будут удалены. Полезно?
— Нейт Уилкинс,

1
Не стесняйтесь запускать консольный проект C # и копировать код внутри класса программы - обязательно замените основной метод. Поработайте с двумя строками (goodUserName и badUserName), и вы сможете увидеть, что делают эти методы и как они работают. Примеры длиннее, чтобы предоставить работоспособное решение, которое можно изменить без разделителей, таких как запятые. Escape-последовательности - это всего лишь один из способов представить одинарные кавычки и обратную косую черту, если вам нужно их проверить.
— Нейт Уилкинс


0

Если вы ищете произвольные строки, а не только символы, вы можете использовать перегрузку IndexOfAny, которая принимает строковые аргументы из нового проекта NLib :

if (s.IndexOfAny("aaa", "bbb", "ccc", StringComparison.Ordinal) >= 0)
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.