Controlla se una stringa contiene uno dei 10 caratteri


107

Sto usando C # e voglio controllare se una stringa contiene uno dei dieci caratteri, *, &, # ecc ecc.

Qual è il modo migliore?


1
Vuoi vedere se uno qualsiasi dei caratteri è presente o se contiene "uno" (ad esempio: esattamente uno) di quei caratteri e solo uno?
Reed Copsey

Risposte:


210

Il seguente sarebbe il metodo più semplice, a mio avviso:

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

O in una forma forse più facile da leggere:

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

A seconda del contesto e delle prestazioni richieste, potresti o meno voler memorizzare nella cache l'array di caratteri.


Quando si crea un'istanza dell'array char, il tipo può essere omesso e verrà dedotto.
Palec

40

Come altri hanno già detto, usa IndexOfAny. Tuttavia, lo userei in questo modo:

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

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

In questo modo non finirai per creare un nuovo array a ogni chiamata. La stringa è anche più facile da analizzare rispetto a una serie di caratteri letterali, IMO.

Ovviamente se lo userai solo una volta, quindi la creazione sprecata non è un problema, potresti usare:

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

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

o

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

Dipende davvero da cosa trovi più leggibile, se vuoi usare i caratteri di punteggiatura altrove e con quale frequenza verrà chiamato il metodo.


EDIT: ecco un'alternativa al metodo di Reed Copsey per scoprire se una stringa contiene esattamente uno dei caratteri.

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

Suppongo che valga la pena memorizzare nella cache l'array di caratteri se le prestazioni sono un problema, ma di nuovo potrebbe non valerne la pena a seconda del contesto.
Noldorin

1
Sì, se lo stai usando solo in un metodo che verrà eseguito una volta che potrebbe non valerne la pena. Tuttavia, penso che migliori la leggibilità e le prestazioni. È possibile utilizzare il ToCharArraymodulo "inline" se necessario, ovviamente.
Jon Skeet

1
@canon: quanto è grande il set? Per set molto, molto piccoli, mi aspetto che Array.Contains sia più veloce. Per set di grandi dimensioni, è probabile che HashSet vinca per miglia.
Jon Skeet

5

Se vuoi solo vedere se contiene caratteri, ti consiglio di usare string.IndexOfAny, come suggerito altrove.

Se vuoi verificare che una stringa contenga esattamente uno dei dieci caratteri e solo uno, diventa un po 'più complicato. Credo che il modo più veloce sarebbe quello di controllare contro un'intersezione, quindi controllare i duplicati.

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

Sì, suppongo che un singolo ciclo sia probabilmente più veloce in questo caso, specialmente con il piccolo insieme di punteggiatura. Sarei curioso di provare a testarlo con stringhe di grandi dimensioni per vedere quale è veramente più veloce.
Reed Copsey

1
Penso che trovare l'intersezione delle due corde dovrà comunque andare carattere per carattere, quindi non vedo come sarebbe più veloce ... e il mio percorso suggerito non solo utilizza un singolo passaggio, ma ha anche il opzione di un "early out". Immagina se il testo è lungo un milione di caratteri, ma i primi due sono entrambi "*" :)
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

Grazie a tutti voi! (E principalmente Jon!): Questo mi ha permesso di scrivere questo:

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

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

poiché stavo cercando un buon modo per rilevare se una determinata stringa fosse effettivamente un prezzo o una frase, come "Troppo basso da visualizzare".


2
So che questo è vecchio, ma per essere chiari questo non è un modo particolarmente buono per abbinare le valute ... Se qualcuno scrivesse "Ke $ ha", corrisponderebbe come prezzo ... Invece fare riferimento a un modo corretto per rilevare valuta definita qui: stackoverflow.com/questions/7214513/...
mcse3010
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.