Sprawdź, czy ciąg zawiera jeden z 10 znaków


108

Używam C # i chcę sprawdzić, czy ciąg zawiera jeden z dziesięciu znaków, *, &, # itd. Itp.

Jaka jest najlepsza droga?


1
Czy chcesz zobaczyć, czy któryś ze znaków tam jest, czy też zawiera „jeden” (tj .: dokładnie jeden) z tych znaków i tylko jeden?
Reed Copsey,

Odpowiedzi:


211

Moim zdaniem najprostsza metoda byłaby następująca:

var match = str.IndexOfAny(new char[] { '*', '&', '#' }) != -1

Lub w możliwie łatwiejszej do odczytania formie:

var match = str.IndexOfAny("*&#".ToCharArray()) != -1

W zależności od kontekstu i wymaganej wydajności możesz lub nie chcesz buforować tablicy char.


Podczas tworzenia wystąpienia tablicy char typ można pominąć i zostanie on wywnioskowany.
Palec

41

Jak powiedzieli inni, użyj IndexOfAny. Jednak użyłbym tego w ten sposób:

private static readonly char[] Punctuation = "*&#...".ToCharArray();

public static bool ContainsPunctuation(string text)
{
    return text.IndexOfAny(Punctuation) >= 0;
}

W ten sposób nie tworzysz nowej tablicy przy każdym wywołaniu. Ciąg jest również łatwiejszy do zeskanowania niż seria literałów znakowych, IMO.

Oczywiście, jeśli zamierzasz użyć tego tylko raz, więc zmarnowana kreacja nie stanowi problemu, możesz użyć:

private const string Punctuation = "*&#...";

public static bool ContainsPunctuation(string text)
{
    return text.IndexOfAny(Punctuation.ToCharArray()) >= 0;
}

lub

public static bool ContainsPunctuation(string text)
{
    return text.IndexOfAny("*&#...".ToCharArray()) >= 0;
}

To naprawdę zależy od tego, co uznasz za bardziej czytelne, czy chcesz użyć znaków interpunkcyjnych w innym miejscu i jak często będzie wywoływana metoda.


EDYCJA: Oto alternatywa dla metody Reeda Copseya do sprawdzania, czy ciąg zawiera dokładnie jeden ze znaków.

private static readonly HashSet<char> Punctuation = new HashSet<char>("*&#...");

public static bool ContainsOnePunctuationMark(string text)
{
    bool seenOne = false;

    foreach (char c in text)
    {
        // TODO: Experiment to see whether HashSet is really faster than
        // Array.Contains. If all the punctuation is ASCII, there are other
        // alternatives...
        if (Punctuation.Contains(c))
        {
            if (seenOne)
            {
                return false; // This is the second punctuation character
            }
            seenOne = true;
        }
    }
    return seenOne;
}

Przypuszczam, że warto buforować tablicę char, jeśli problemem jest wydajność, ale z drugiej strony może to nie być tego warte w zależności od kontekstu.
Noldorin

1
Tak, jeśli używasz go tylko w metodzie, która zostanie wykonana raz, może nie być tego warta. Myślę jednak, że poprawia to czytelność, a także wydajność. Oczywiście w ToCharArrayrazie potrzeby można użyć formularza „inline”.
Jon Skeet

1
@canon: Jak duży jest zestaw? Przy bardzo, bardzo małych zestawach spodziewałbym się, że Array.Contains będzie szybsze. W przypadku dużych zestawów HashSet prawdopodobnie wygra o mile.
Jon Skeet

5

Jeśli chcesz tylko sprawdzić, czy zawiera jakiś znak, polecam użycie string.IndexOfAny, jak zasugerowano w innym miejscu.

Jeśli chcesz sprawdzić, czy ciąg zawiera dokładnie jeden z dziesięciu znaków i tylko jeden, sprawa staje się nieco bardziej skomplikowana. Uważam, że najszybszym sposobem byłoby sprawdzenie skrzyżowania, a następnie sprawdzenie duplikatów.

private static char[] characters = new char [] { '*','&',... };

public static bool ContainsOneCharacter(string text)
{
    var intersection = text.Intersect(characters).ToList();
    if( intersection.Count != 1)
        return false; // Make sure there is only one character in the text

    // Get a count of all of the one found character
    if (1 == text.Count(t => t == intersection[0]) )
        return true;

    return false;
}

Tak - przypuszczam, że pojedyncza pętla jest prawdopodobnie szybsza w tym przypadku, szczególnie z małym zestawem znaków interpunkcyjnych. Byłbym ciekawy, aby spróbować to przetestować z dużymi ciągami, aby zobaczyć, który jest naprawdę szybszy.
Reed Copsey,

1
Myślę, że znalezienie przecięcia się dwóch ciągów i tak będzie musiało przebiegać znak po znaku, więc nie widzę, jak byłoby szybsze ... a moja sugerowana trasa nie tylko wykorzystuje pojedyncze przejście, ale także ma opcja „wczesnego wyjścia”. Wyobraź sobie, że tekst ma milion znaków, ale oba pierwsze dwa to „*” :)
Jon Skeet,


1
var specialChars = new[] {'\\', '/', ':', '*', '<', '>', '|', '#', '{', '}', '%', '~', '&'};

foreach (var specialChar in specialChars.Where(str.Contains))
{
    Console.Write(string.Format("string must not contain {0}", specialChar));
}

0

Dzięki Wam wszystkim! (I głównie Jon!): To pozwoliło mi napisać to:

    private static readonly char[] Punctuation = "$€£".ToCharArray();

    public static bool IsPrice(this string text)
    {
        return text.IndexOfAny(Punctuation) >= 0;
    }

ponieważ szukałem dobrego sposobu na wykrycie, czy dany ciąg jest w rzeczywistości ceną lub zdaniem, na przykład „Za nisko, by wyświetlić”.


2
Wiem, że to jest stare, ale żeby było jasne, nie jest to szczególnie dobry sposób dopasowywania walut ... Gdybyś miał napisać „Ke $ ha”, odpowiadałoby to jako cena ... Zamiast tego odnieś się do jednego właściwego sposobu wykryj walutę zdefiniowaną tutaj: stackoverflow.com/questions/7214513/ ...
mcse3010
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.