Jak sprawdzić, czy ciąg zawiera którykolwiek z ciągów


101

Chcę sprawdzić, czy String s zawiera „a” lub „b” lub „c” w języku C #. Szukam ładniejszego rozwiązania niż używanie

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

1
W złożonych przypadkach sprawdź triestrukturę danych.
Miserable Variable

Odpowiedzi:


45

Jeśli szukasz pojedynczych znaków, możesz użyć String.IndexOfAny().

Jeśli potrzebujesz dowolnych ciągów, nie znam metody .NET, która umożliwiłaby to „bezpośrednio”, chociaż zadziałałoby wyrażenie regularne.


97

Cóż, zawsze jest to:

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

    return false;
}

Stosowanie:

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

||Jednak nic nie dorówna wydajności twojego łańcucha porównań.


14
Dodanie nowej, krótkiej składni do tego fajnego rozwiązania public static bool ContainsAny(this string haystack, params string[] needles) { return needles.Any(haystack.Contains); }
simonkaspers1

Proste i oczywiste rozwiązanie. Ale czy jest jakaś dobra, gotowa do użycia implementacja, która nie wymaga wielu iteracji w ciągu stogu siana? Mogę to zaimplementować samodzielnie, iterując po ciągach znaków stogu siana i porównując kolejno pierwsze znaki igieł za jednym razem, ale nie mogę uwierzyć, że tak banalne rozwiązanie nie zostało jeszcze zaimplementowane w jakiejś znanej bibliotece NuGet.
RollerKostr

@RollerKostr To nie jest wbudowane w C # (jeszcze), więc po co dodawać dodatkowe zależności w projekcie dla tak prostego rozwiązania?
jmdon

74

Oto rozwiązanie LINQ, które jest praktycznie takie samo, ale bardziej skalowalne:

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

3
Jest to skalowalne w tym sensie, że łatwo jest dodawać postacie, a nie w sensie wydajności ... :)
Guffa

2
Ach tak, oczywiście. Być może „bardziej rozszerzalny” byłby lepszym doborem słów.
Jeff Mercado,

Występ nie będzie straszny. W każdym razie lepsze niż zinterpretowane wyrażenie regularne.
Steven Sudit

Świetna odpowiedź, aby uzyskać kompletność, możesz najpierw podzielić przychodzący ciąg na tablicę, np .: var splitStringArray = someString.Split (''); Następnie możesz zrobić coś takiego: if (someStringArray.Any (s => otherString.Contains (s))) {// zrób coś} Mam nadzieję, że pomoże to komuś w zrozumieniu.
Tahir Khalid

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

21

Możesz spróbować z wyrażeniem regularnym

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

1
+1, chociaż ponieważ szuka pojedynczych znaków, rozwiązanie linq lub indexOfAny może być bardziej wydajne.
Joel Coehoorn

+1 dla wyrażenia regularnego. po to bym poszedł, gdyby nie było IndexOfAny
Stavros

1
Wyrażenia regularne są do tego przesadą.
Steven Sudit

3
Co sprawia, że ​​ludzie mówią, że wyrażenia regularne są z tego powodu przesadne? Jeśli wyrażenie regularne jest kompilowane raz i używane wiele razy, a masz łańcuchy zawierające tylko c w nich lub c blisko początku i a, b blisko końca, to wyrażenie regularne byłoby znacznie bardziej wydajne.
bruceboughton

Nie działa w przypadku znaków specjalnych, takich jak -, '"
.`

15

Jeśli potrzebujesz ContainsAny z określonym StringComparison(na przykład w celu zignorowania wielkości liter), możesz użyć tej metody 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);
    }
}

Użycie z 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
Tylko jedna uwaga, aby poprawić tę odpowiedź. Możesz napisać go jeszcze bardziej elegancko za pomocą słowa kluczowego params: ContainsAny (ten ciąg wejściowy, StringComparison CompareType, parametr string [] zawieraSłowa kluczowe) i używać go jak input.ContainsAny (substrings, StringComparison.CurrentCultureIgnoreCase, "string", "many substrings" ... itp.)
Roma Borodov,

8

To „przyjemniejsze rozwiązanie” i całkiem proste

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

4

Ponieważ ciąg jest zbiorem znaków, możesz użyć na nich metod rozszerzających LINQ:

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

Spowoduje to jednorazowe przeskanowanie ciągu i zatrzymanie się przy pierwszym wystąpieniu, zamiast przeszukiwania łańcucha raz dla każdego znaku, aż do znalezienia dopasowania.

Można tego również użyć do dowolnego wyrażenia, które lubisz, na przykład sprawdzania zakresu znaków:

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

Zgoda. To rozwiązuje problem wielu skanów, gdy pierwsze warunki nie są zgodne. Zastanawiam się, co to jest narzut lambda? Jednak raz nie powinno być dużo.
bruceboughton

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

3
List<string> includedWords = new List<string>() { "a", "b", "c" };
bool string_contains_words = includedWords.Exists(o => s.Contains(o));

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

Anydla każdego, Alldla każdego


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
Metody HasWantedCharacters akceptują dwa lub trzy ciągi. Pierwszy ciąg, który chcemy sprawdzić pod kątem określonych znaków. Drugi ciąg, wszystkie znaki, których będziemy szukać w pierwszym. Przeciążona metoda dostarcza dane wyjściowe do obiektu wywołującego (tj. Main) jako trzeci ciąg. Zagnieżdżona instrukcja foreach przechodzi przez każdy znak w źródle i porównuje go jeden po drugim; z tymi postaciami, których szukamy. Jeśli jeden ze znaków zostanie znaleziony, zwraca wartość true. Przeciążona metoda generuje ciąg znalezionych znaków pasujących do tych, dla których sprawdzono, ale nie zwróci, dopóki wszystkie nie zostaną usunięte. Pomocny?
Nate Wilkins

1
Zapraszam do uruchomienia projektu konsoli C # i skopiowania kodu wewnątrz klasy programu - pamiętaj o zastąpieniu metody main. Majstrować przy dwóch ciągach znaków (goodUserName i badUserName), a zobaczysz, co robią te metody i jak działają. Przykłady są dłuższe, aby zapewnić działające rozwiązanie, które można modyfikować bez ograniczników, takich jak przecinki. Sekwencje ucieczki to tylko jeden sposób na przedstawienie pojedynczego cudzysłowu i ukośnika odwrotnego, jeśli chcesz je sprawdzić.
Nate Wilkins


0

Jeśli szukasz dowolnych ciągów, a nie tylko znaków, możesz użyć przeciążenia IndexOfAny, które pobiera argumenty ciągów z nowego projektu NLib :

if (s.IndexOfAny("aaa", "bbb", "ccc", StringComparison.Ordinal) >= 0)
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.