Compruebe si una cadena contiene uno de los 10 caracteres

107

Estoy usando C # y quiero verificar si una cadena contiene uno de diez caracteres, *, &, #, etc., etc.

¿Cuál es la mejor manera?

Jade M
fuente
1
¿Quiere ver si alguno de los caracteres está ahí, o si contiene "uno" (es decir, exactamente uno) de esos caracteres, y solo uno?
Reed Copsey

Respuestas:

210

El siguiente sería el método más simple, en mi opinión:

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

O en una forma posiblemente más fácil de leer:

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

Según el contexto y el rendimiento requeridos, es posible que desee o no almacenar en caché la matriz de caracteres.

Noldorin
fuente
Al crear una instancia de la matriz char, el tipo se puede omitir y se infiere.
Palec
40

Como han dicho otros, use IndexOfAny. Sin embargo, lo usaría de esta manera:

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

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

De esa manera, no terminará creando una nueva matriz en cada llamada. La cadena también es más fácil de escanear que una serie de caracteres literales, en mi opinión.

Por supuesto, si solo va a usar esto una vez, para que la creación desperdiciada no sea un problema, puede usar:

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

Realmente depende de cuál le resulte más legible, si desea utilizar los caracteres de puntuación en otro lugar y con qué frecuencia se llamará al método.


EDITAR: Aquí hay una alternativa al método de Reed Copsey para averiguar si una cadena contiene exactamente uno de los 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
fuente
Supongo que vale la pena almacenar en caché la matriz de caracteres si el rendimiento es un problema, pero, de nuevo, es posible que no valga la pena según el contexto.
Noldorin
1
Sí, si solo lo está usando en un método que se ejecutará una vez, puede que no valga la pena. Sin embargo, creo que mejora la legibilidad y el rendimiento. Por ToCharArraysupuesto, puede utilizar el formulario "en línea" si es necesario.
Jon Skeet
1
@canon: ¿Qué tan grande es el set? Para conjuntos muy, muy pequeños, esperaría que Array.Contains sea más rápido. Para conjuntos grandes, es probable que HashSet gane por millas.
Jon Skeet
5

Si solo desea ver si contiene algún carácter, le recomiendo usar string.IndexOfAny, como se sugiere en otra parte.

Si desea verificar que una cadena contiene exactamente uno de los diez caracteres, y solo uno, entonces se vuelve un poco más complicado. Creo que la forma más rápida sería verificar una intersección y luego verificar si hay duplicados.

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
fuente
Sí, supongo que un solo bucle es probablemente más rápido en este caso, especialmente con el pequeño conjunto de puntuación. Tendría curiosidad por probar esto con cadenas grandes para ver cuál es realmente más rápido.
Reed Copsey
1
Creo que encontrar la intersección de las dos cadenas tendrá que ir carácter por carácter de todos modos, así que no puedo ver cómo sería más rápido ... y mi ruta sugerida no solo usa una sola pasada, sino que también tiene la opción de "salida anticipada". Imagínese si el texto tiene un millón de caracteres, pero los dos primeros son "*" :)
Jon Skeet
1
var specialChars = new[] {'\\', '/', ':', '*', '<', '>', '|', '#', '{', '}', '%', '~', '&'};

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

¡Gracias a todos ustedes! (¡Y principalmente Jon!): Esto me permitió escribir esto:

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

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

ya que estaba buscando una buena manera de detectar si una determinada cadena era en realidad un precio o una oración, como 'Demasiado bajo para mostrar'.

BernardG
fuente
2
Sé que esto es antiguo, pero para que quede claro, esta no es una forma particularmente buena de hacer coincidir las monedas ... Si alguien escribiera "Ke $ ha", coincidiría como precio ... En su lugar, consulte una forma adecuada de detectar la moneda definida aquí: stackoverflow.com/questions/7214513/…
mcse3010