Vérifiez si une chaîne contient l'un des 10 caractères

108

J'utilise C # et je veux vérifier si une chaîne contient l'un des dix caractères, *, &, # etc. etc.

Quel est le meilleur moyen?

Jade M
la source
1
Voulez-vous voir si l'un des caractères est présent ou s'il contient «un» (c'est-à-dire exactement un) de ces caractères, et un seul?
Reed Copsey

Réponses:

211

Ce qui suit serait la méthode la plus simple, à mon avis:

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

Ou sous une forme éventuellement plus facile à lire:

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

Selon le contexte et les performances requis, vous pouvez ou non vouloir mettre en cache le tableau char.

Noldorin
la source
Lors de l'instanciation du tableau char, le type peut être omis et il sera déduit.
Palec
41

Comme d'autres l'ont dit, utilisez IndexOfAny. Cependant, je l'utiliserais de cette manière:

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

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

De cette façon, vous ne finissez pas par créer un nouveau tableau à chaque appel. La chaîne est également plus facile à analyser qu'une série de littéraux de caractères, IMO.

Bien sûr, si vous ne l'utilisez qu'une seule fois, donc la création gaspillée n'est pas un problème, vous pouvez soit utiliser:

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

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

ou

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

Cela dépend vraiment de ce que vous trouvez le plus lisible, si vous souhaitez utiliser les caractères de ponctuation ailleurs et à quelle fréquence la méthode sera appelée.


EDIT: Voici une alternative à la méthode de Reed Copsey pour savoir si une chaîne contient exactement l'un des caractères.

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;
}
Jon Skeet
la source
Je suppose que cela vaut la peine de mettre en cache le tableau char si les performances sont un problème, mais là encore, cela ne vaut peut-être pas la peine en fonction du contexte.
Noldorin
1
Oui, si vous ne l'utilisez que dans une méthode qui sera exécutée une fois, cela ne vaut peut-être pas la peine. Cependant, je pense que cela améliore la lisibilité ainsi que les performances. Vous pouvez ToCharArraybien sûr utiliser le formulaire "en ligne" si nécessaire.
Jon Skeet
1
@canon: Quelle est la taille de l'ensemble? Pour de très, très petits ensembles, je m'attendrais à ce que Array.Contains soit plus rapide. Pour les grands sets, HashSet est susceptible de gagner par miles.
Jon Skeet le
5

Si vous voulez juste voir s'il contient un caractère, je vous recommande d'utiliser string.IndexOfAny, comme suggéré ailleurs.

Si vous voulez vérifier qu'une chaîne contient exactement l'un des dix caractères, et un seul, cela devient un peu plus compliqué. Je pense que le moyen le plus rapide serait de vérifier par rapport à une intersection, puis de vérifier les doublons.

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;
}
Reed Copsey
la source
Ouais - je suppose qu'une seule boucle est probablement plus rapide dans ce cas, surtout avec le petit jeu de ponctuation. Je serais curieux d'essayer de tester cela avec de grandes chaînes pour voir laquelle est vraiment plus rapide.
Reed Copsey
1
Je pense que trouver l'intersection des deux chaînes devra de toute façon aller caractère par caractère, donc je ne peux pas voir comment ce serait plus rapide ... et mon itinéraire suggéré utilise non seulement un seul passage, mais a également le option d'un "early out". Imaginez si le texte comporte un million de caractères, mais que les deux premiers sont tous les deux "*" :)
Jon Skeet
1
var specialChars = new[] {'\\', '/', ':', '*', '<', '>', '|', '#', '{', '}', '%', '~', '&'};

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

Merci à tous! (Et surtout Jon!): Cela m'a permis d'écrire ceci:

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

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

car je cherchais un bon moyen de détecter si une certaine chaîne était en fait un prix ou une phrase, comme «Trop bas pour afficher».

BernardG
la source
2
Je sais que c'est vieux, mais pour être clair, ce n'est pas un moyen particulièrement efficace de faire correspondre les devises ... Si quelqu'un écrivait "Ke $ ha", cela correspondrait à un prix ... détecter la devise définie ici: stackoverflow.com/questions/7214513/…
mcse3010