Verifique se uma string contém um de 10 caracteres

107

Estou usando C # e quero verificar se uma string contém um de dez caracteres, *, &, # etc etc.

Qual é a melhor maneira?

Jade M
fonte
1
Você quer ver se algum dos caracteres está lá, ou se contém "um" (ou seja: Exatamente um) desses caracteres, e apenas um?
Reed Copsey

Respostas:

210

O seguinte seria o método mais simples, na minha opinião:

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

Ou de uma forma possivelmente mais fácil de ler:

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

Dependendo do contexto e do desempenho necessários, você pode ou não querer armazenar em cache o array char.

Noldorin
fonte
Ao instanciar a matriz char, o tipo pode ser omitido e será inferido.
Palec
40

Como já foi dito, use IndexOfAny. No entanto, eu o usaria desta forma:

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

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

Dessa forma, você não acaba criando um novo array a cada chamada. A string também é mais fácil de digitalizar do que uma série de literais de caracteres, IMO.

Claro, se você vai usar isso apenas uma vez, para que a criação desperdiçada não seja um problema, você pode usar:

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

Realmente depende de qual você achar mais legível, se deseja usar os caracteres de pontuação em outro lugar e com que freqüência o método será chamado.


EDIT: Aqui está uma alternativa ao método de Reed Copsey para descobrir se uma string contém exatamente um dos caracteres.

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
fonte
Suponho que vale a pena armazenar em cache a matriz char se o desempenho for um problema, mas, novamente, pode não valer a pena dependendo do contexto.
Noldorin
1
Sim, se você estiver usando apenas em um método que será executado uma vez, pode não valer a pena. No entanto, acho que melhora a legibilidade e também o desempenho. Você pode usar o ToCharArrayformulário "embutido" se necessário, é claro.
Jon Skeet
1
@canon: Qual é o tamanho do conjunto? Para conjuntos muito, muito pequenos, eu esperaria que Array.Contains fosse mais rápido. Para grandes sets, o HashSet provavelmente vencerá por milhas.
Jon Skeet,
5

Se você deseja apenas ver se contém algum caractere, recomendo usar string.IndexOfAny, conforme sugerido em outro lugar.

Se você quiser verificar se uma string contém exatamente um dos dez caracteres, e apenas um, fica um pouco mais complicado. Acredito que a maneira mais rápida seria verificar uma interseção e, em seguida, verificar se há duplicatas.

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
fonte
Sim - suponho que um único loop é provavelmente mais rápido neste caso, especialmente com o pequeno conjunto de pontuação. Eu ficaria curioso para tentar testar isso com cordas grandes para ver qual é realmente mais rápido.
Reed Copsey,
1
Acho que encontrar a interseção das duas cordas vai ter que ir caractere por caractere de qualquer maneira, então não consigo ver como seria mais rápido ... e minha rota sugerida não usa apenas uma única passagem, mas também tem o opção de "saída antecipada". Imagine se o texto tivesse um milhão de caracteres, mas os dois primeiros fossem "*" :)
Jon Skeet
1
var specialChars = new[] {'\\', '/', ':', '*', '<', '>', '|', '#', '{', '}', '%', '~', '&'};

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

Obrigado a todos vocês! (E principalmente Jon!): Isso me permitiu escrever isto:

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

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

porque estava procurando uma boa maneira de detectar se uma determinada string era realmente um preço ou uma frase, como 'Muito baixo para exibir'.

BernardG
fonte
2
Eu sei que isso é antigo, mas para ficar claro, esta não é uma maneira particularmente boa de combinar moedas ... Se você tivesse alguém escrevendo "Ke $ ha", corresponderia como um preço ... Em vez disso, consulte uma maneira adequada de detectar a moeda definida aqui: stackoverflow.com/questions/7214513/…
mcse3010