[c#] Insensible a mayúsculas y minúsculas 'Contiene (cadena)'


Answers

Puede usar el método String.IndexOf y pasar StringComparison.OrdinalIgnoreCase como el tipo de búsqueda a usar:

string title = "STRING";
bool contains = title.IndexOf("string", StringComparison.OrdinalIgnoreCase) >= 0;

Aún mejor es definir un nuevo método de extensión para la cadena:

public static class StringExtensions
{
    public static bool Contains(this string source, string toCheck, StringComparison comp)
    {
        return source?.IndexOf(toCheck, comp) >= 0;
    }
}

Tenga en cuenta que esa propagación nula ?. está disponible desde C # 6.0 (VS 2015), para versiones anteriores use

   if(source==null) return false;
   return source.IndexOf(toCheck, comp) >= 0;

USO:

string title = "STRING";
bool contains = title.Contains("string", StringComparison.OrdinalIgnoreCase);
Question

¿Hay alguna manera de hacer que el siguiente retorno sea cierto?

string title = "ASTRINGTOTEST";
title.Contains("string");

No parece haber una sobrecarga que me permita establecer la sensibilidad de la mayúscula / minúscula ... Actualmente ME SUPERIORO a ambas, pero es una tontería.

ACTUALIZAR
La tontería a la que me refiero es a los problemas que vienen con la carcasa hacia arriba y hacia abajo.

ACTUALIZAR
Esta pregunta es antigua y desde entonces me he dado cuenta de que pedí una respuesta simple para un tema realmente vasto y difícil si te interesa investigarlo por completo.
Para la mayoría de los casos, en bases de código en inglés, monolingües, this respuesta será suficiente. Sospecho que porque la mayoría de las personas que entran aquí caen en esta categoría, esta es la respuesta más popular.
This embargo, This respuesta plantea el problema inherente de que no podemos comparar mayúsculas y minúsculas hasta que sepamos que ambos textos son de la misma cultura y sabemos cuál es esa cultura. Esta es quizás una respuesta menos popular, pero creo que es más correcta y es por eso que la marqué como tal.




El método InStr del ensamblado VisualBasic es el mejor si tiene alguna inquietud acerca de la internacionalización (o puede volver a implementarlo). Al mirar en él, dotNeetPeek muestra que no solo cuenta para mayúsculas y minúsculas, sino también para caracteres de tipo kana y de ancho completo frente a medio ancho (mayormente relevantes para idiomas asiáticos, aunque también hay versiones de ancho completo del alfabeto romano). ) Me estoy saltando algunos detalles, pero mira el método privado InternalInStrText :

private static int InternalInStrText(int lStartPos, string sSrc, string sFind)
{
  int num = sSrc == null ? 0 : sSrc.Length;
  if (lStartPos > num || num == 0)
    return -1;
  if (sFind == null || sFind.Length == 0)
    return lStartPos;
  else
    return Utils.GetCultureInfo().CompareInfo.IndexOf(sSrc, sFind, lStartPos, CompareOptions.IgnoreCase | CompareOptions.IgnoreKanaType | CompareOptions.IgnoreWidth);
}



Puede usar la función string.indexof () . Esto será insensible a mayúsculas y minúsculas




En última instancia, una operación genérica de "contiene" se reduce a una función como esta,

/// <summary>
/// Determines whether the source contains the sequence.
/// </summary>
/// <typeparam name="T">The type of the items in the sequences.</typeparam>
/// <param name="sourceEnumerator">The source enumerator.</param>
/// <param name="sequenceEnumerator">The sequence enumerator.</param>
/// <param name="equalityComparer">An equality comparer.</param>
/// <remarks>
/// An empty sequence will return <c>true</c>.
/// The sequence must support <see cref="IEnumerator.Reset"/>
/// if it does not begin the source.
/// </remarks>
/// <returns>
/// <c>true</c> if the source contains the sequence;
/// otherwise <c>false</c>.
/// </returns>
public static bool Contains<T>(
    IEnumerator<T> sourceEnumerator,
    IEnumerator<T> sequenceEnumerator,
    IEqualityComparer<T> equalityComparer)
{
    if (equalityComparer == null)
    {
        equalityComparer = EqualityComparer<T>.Default;
    }

    while (sequenceEnumerator.MoveNext())
    {
        if (sourceEnumerator.MoveNext())
        {
            if (!equalityComparer.Equals(
                sourceEnumerator.Current,
                sequenceEnumerator.Current))
            {
                sequenceEnumerator.Reset();
            }
        }
        else
        {
            return false;
        }
    }

    return true;
}

esto puede ser trivialmente envuelto en una versión de extensión que acepte IEnumerable esta manera,

public static bool Contains<T>(
        this IEnumerable<T> source,
        IEnumerable<T> sequence,
        IEqualityComparer<T> equalityComparer = null)
{
    if (sequence == null)
    {
        throw new ArgumentNullException("sequence");
    }

    using(var sequenceEnumerator = sequence.GetEnumerator())
    using(var sourceEnumerator = source.GetEnumerator())
    {
        return Contains(
            sourceEnumerator,
            sequenceEnumerator,
            equalityComparer);
    }
}

Ahora, esto funcionará para la comparación ordinal de cualquier secuencia, incluidas las cadenas, ya que la string implementa IEnumerable<char> ,

// The optional parameter ensures the generic overload is invoked
// not the string.Contains() implementation.
"testable".Contains("est", EqualityComparer<char>.Default)

Sin embargo, como sabemos, las cadenas no son genéricas, están especializadas. Hay dos factores clave en juego.

  1. El problema de la "carcasa" que tiene varios casos de borde dependientes del idioma.
  2. El tema bastante complicado de cómo un conjunto de "Elementos de texto" (letras / números / símbolos, etc.) están representados por puntos de código Unicode y qué secuencias válidas de caracteres pueden representar una cadena dada, los detalles se expanden en these answers .

El efecto neto es el mismo. Las cadenas que puede afirmar que son lingüísticamente iguales pueden representarse válidamente mediante diferentes combinaciones de caracteres. Además, las reglas de validez cambian entre culturas.

Todo esto lleva a una implementación especializada de "Contiene" basada en cadenas como esta.

using System.Globalization;

public static bool Contains(
         this string source,
         string value,
         CultureInfo culture = null,
         CompareOptions options = CompareOptions.None)
{
    if (value == null)
    {
        throw new ArgumentNullException("value");
    }

    var compareInfo = culture == null ? 
            CultureInfo.CurrentCulture.CompareInfo :
            culture.CompareInfo;

    var sourceEnumerator = StringInfo.GetTextElementEnumerator(source);
    var sequenceEnumerator = StringInfo.GetTextElementEnumerator(value);

    while (sequenceEnumerator.MoveNext())
    {
        if (sourceEnumerator.MoveNext())
        {
            if (!(compareInfo.Compare(
                    sourceEnumerator.Current,
                    sequenceEnumerator.Current,
                    options) == 0))
            {
                sequenceEnumerator.Reset();
            }
        }
        else
        {
            return false;
        }
    }

    return true;
}

Esta función se puede utilizar para realizar un "contenedor" específico para cada caso, insensible a las mayúsculas y minúsculas, que funcionará, cualquiera que sea la normalización de las cadenas. p.ej

"testable".Contains("EST", StringComparer.CurrentCultureIgnoreCase)



Solución alternativa usando Regex:

bool contains = Regex.IsMatch("StRiNG to search", "string", RegexOptions.IgnoreCase);

darse cuenta

Como @cHao ha señalado en su comentario, hay situaciones que harán que esta solución arroje resultados incorrectos. Asegúrese de saber lo que está haciendo antes de implementar esta solución al azar.




Esto es limpio y simple.

Regex.IsMatch(file,fileNamestr,RegexOptions.IgnoreCase)



Un problema con la respuesta es que emitirá una excepción si una cadena es nula. Puede agregar eso como un cheque para que no lo haga:

public static bool Contains(this string source, string toCheck, StringComparison comp)
{
    if (string.IsNullOrEmpty(toCheck) || string.IsNullOrEmpty(source))
        return true;

    return source.IndexOf(toCheck, comp) >= 0;
} 



Solo así:

string s="AbcdEf";
if(s.ToLower().Contains("def"))
{
    Console.WriteLine("yes");
}



Esto es bastante similar a otro ejemplo aquí, pero he decidido simplificar enum a bool, primario porque normalmente no se necesitan otras alternativas. Aquí está mi ejemplo:

public static class StringExtensions
{
    public static bool Contains(this string source, string toCheck, bool bCaseInsensitive )
    {
        return source.IndexOf(toCheck, bCaseInsensitive ? StringComparison.OrdinalIgnoreCase : StringComparison.Ordinal) >= 0;
    }
}

Y el uso es algo así como:

if( "main String substring".Contains("SUBSTRING", true) )
....



public static class StringExtension
{
    #region Public Methods

    public static bool ExContains(this string fullText, string value)
    {
        return ExIndexOf(fullText, value) > -1;
    }

    public static bool ExEquals(this string text, string textToCompare)
    {
        return text.Equals(textToCompare, StringComparison.OrdinalIgnoreCase);
    }

    public static bool ExHasAllEquals(this string text, params string[] textArgs)
    {
        for (int index = 0; index < textArgs.Length; index++)
            if (ExEquals(text, textArgs[index]) == false) return false;
        return true;
    }

    public static bool ExHasEquals(this string text, params string[] textArgs)
    {
        for (int index = 0; index < textArgs.Length; index++)
            if (ExEquals(text, textArgs[index])) return true;
        return false;
    }

    public static bool ExHasNoEquals(this string text, params string[] textArgs)
    {
        return ExHasEquals(text, textArgs) == false;
    }

    public static bool ExHasNotAllEquals(this string text, params string[] textArgs)
    {
        for (int index = 0; index < textArgs.Length; index++)
            if (ExEquals(text, textArgs[index])) return false;
        return true;
    }

    /// <summary>
    /// Reports the zero-based index of the first occurrence of the specified string
    /// in the current System.String object using StringComparison.InvariantCultureIgnoreCase.
    /// A parameter specifies the type of search to use for the specified string.
    /// </summary>
    /// <param name="fullText">
    /// The string to search inside.
    /// </param>
    /// <param name="value">
    /// The string to seek.
    /// </param>
    /// <returns>
    /// The index position of the value parameter if that string is found, or -1 if it
    /// is not. If value is System.String.Empty, the return value is 0.
    /// </returns>
    /// <exception cref="ArgumentNullException">
    /// fullText or value is null.
    /// </exception>
    public static int ExIndexOf(this string fullText, string value)
    {
        return fullText.IndexOf(value, StringComparison.OrdinalIgnoreCase);
    }

    public static bool ExNotEquals(this string text, string textToCompare)
    {
        return ExEquals(text, textToCompare) == false;
    }

    #endregion Public Methods
}



Manera simple para principiante:

title.ToLower().Contains("string");//of course "string" is lowercase.



Related