Überprüfen Sie, ob eine Zeichenfolge eines von 10 Zeichen enthält


107

Ich verwende C # und möchte überprüfen, ob eine Zeichenfolge eines von zehn Zeichen *, &, # usw. usw. enthält.

Was ist der beste Weg?


1
Möchten Sie sehen, ob eines der Zeichen vorhanden ist oder ob es "eines" (dh genau eines) dieser Zeichen und nur eines enthält?
Reed Copsey

Antworten:


210

Folgendes wäre aus meiner Sicht die einfachste Methode:

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

Oder in einer möglicherweise leichter lesbaren Form:

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

Je nach Kontext und erforderlicher Leistung möchten Sie das char-Array möglicherweise zwischenspeichern oder nicht.


Beim Instanziieren des char-Arrays kann der Typ weggelassen werden und daraus abgeleitet werden.
Palec

40

Wie andere gesagt haben, verwenden Sie IndexOfAny. Ich würde es jedoch folgendermaßen verwenden:

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

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

Auf diese Weise erstellen Sie nicht bei jedem Aufruf ein neues Array. Die Zeichenfolge ist auch einfacher zu scannen als eine Reihe von Zeichenliteralen, IMO.

Wenn Sie dies nur einmal verwenden möchten, damit die verschwendete Erstellung kein Problem darstellt, können Sie natürlich Folgendes verwenden:

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

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

oder

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

Es hängt wirklich davon ab, welche Sie besser lesbar finden, ob Sie die Satzzeichen an anderer Stelle verwenden möchten und wie oft die Methode aufgerufen wird.


BEARBEITEN: Hier ist eine Alternative zu Reed Copseys Methode, um herauszufinden, ob eine Zeichenfolge genau eines der Zeichen enthält.

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

Ich nehme an, es lohnt sich, das char-Array zwischenzuspeichern, wenn die Leistung ein Problem darstellt, aber es lohnt sich je nach Kontext möglicherweise nicht.
Noldorin

1
Ja, wenn Sie es nur in einer Methode verwenden, die ausgeführt wird, wenn es sich nicht lohnt. Ich denke jedoch, dass es sowohl die Lesbarkeit als auch die Leistung verbessert. ToCharArrayBei Bedarf können Sie natürlich auch das Formular "Inline" verwenden.
Jon Skeet

1
@canon: Wie groß ist das Set? Für sehr, sehr kleine Mengen würde ich erwarten, dass Array.Contains schneller ist. Bei großen Sets wird HashSet wahrscheinlich meilenweit gewinnen.
Jon Skeet

5

Wenn Sie nur sehen möchten, ob es ein Zeichen enthält, würde ich die Verwendung von string.IndexOfAny empfehlen, wie an anderer Stelle vorgeschlagen.

Wenn Sie überprüfen möchten, ob eine Zeichenfolge genau eines der zehn Zeichen und nur eines enthält, wird dies etwas komplizierter. Ich glaube, der schnellste Weg wäre, gegen eine Kreuzung zu prüfen und dann nach Duplikaten zu suchen.

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

Ja - ich nehme an, eine einzelne Schleife ist in diesem Fall wahrscheinlich schneller, insbesondere bei kleinen Satzzeichen. Ich wäre neugierig, dies mit großen Zeichenfolgen zu testen, um zu sehen, welche wirklich schneller ist.
Reed Copsey

1
Ich denke, dass das Finden des Schnittpunkts der beiden Zeichenfolgen ohnehin Zeichen für Zeichen gehen muss, daher kann ich nicht sehen, wie es schneller sein würde ... und meine vorgeschlagene Route verwendet nicht nur einen einzelnen Durchgang, sondern hat auch den Option eines "Early Out". Stellen Sie sich vor, der Text ist eine Million Zeichen lang, aber die ersten beiden sind beide "*" :)
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

Vielen Dank an euch alle! (Und hauptsächlich Jon!): Dadurch konnte ich Folgendes schreiben:

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

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

Ich suchte nach einer guten Möglichkeit, um festzustellen, ob eine bestimmte Zeichenfolge tatsächlich ein Preis oder ein Satz ist, z. B. "Zu niedrig zum Anzeigen".


2
Ich weiß, dass dies alt ist, aber um klar zu sein, ist dies kein besonders guter Weg, um Währungen abzugleichen ... Wenn Sie jemanden "Ke $ ha" schreiben lassen würden, würde dies als Preis übereinstimmen ... Verweisen Sie stattdessen auf einen geeigneten Weg zu Hier definierte Währung erkennen: stackoverflow.com/questions/7214513/…
mcse3010
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.