Bez względu na wielkość liter „Contains (string)”


2907

Czy istnieje sposób, aby następujący zwrot był prawdziwy?

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

Wydaje się, że nie ma przeciążenia, które pozwala mi ustawić rozróżnianie wielkości liter. Obecnie UPPERCASE je oba, ale to po prostu głupie (przez co odnoszę się do problemów i18n , które są związane z obudową w górę iw dół).

AKTUALIZACJA
To pytanie jest starożytne i od tego czasu zdałem sobie sprawę, że poprosiłem o prostą odpowiedź na naprawdę obszerny i trudny temat, jeśli chcesz w pełni go zbadać.
W większości przypadków, w mono-językowej, bazy kod English to odpowiedź wystarczy. Podejrzewam, ponieważ większość osób przyjeżdżających tutaj należy do tej kategorii, jest to najpopularniejsza odpowiedź.
Ta odpowiedź rodzi jednak nieodłączny problem polegający na tym, że nie możemy porównać tekstu bez rozróżniania wielkości liter, dopóki nie dowiemy się, że oba teksty są tą samą kulturą i nie wiemy, czym jest ta kultura. To może być mniej popularna odpowiedź, ale myślę, że jest bardziej poprawna i dlatego oznaczyłem ją jako taką.

Odpowiedzi:


1398

Aby sprawdzić, czy ciąg paragraphzawiera ciąg word(dzięki @QuarterMeister)

culture.CompareInfo.IndexOf(paragraph, word, CompareOptions.IgnoreCase) >= 0

Gdzie culturejest przykład CultureInfoopisu języka, w którym napisany jest tekst.

To rozwiązanie jest przejrzyste w odniesieniu do definicji rozróżniania wielkości liter, która zależy od języka . Na przykład w języku angielskim używane są znaki Ioraz iw wersjach wielkiej i małej dziewiątej litery, natomiast w języku tureckim te znaki są stosowane w jedenastej i dwunastej litery 29-literowego alfabetu. Turecka wielka wersja „i” to nieznany znak „İ”.

Stąd ciągi znaków tini TINto samo słowo po angielsku , ale różne słowa po turecku . Jak rozumiem, jedno oznacza „duch”, a drugie to słowo onomatopei. (Turcy, poprawcie mnie, jeśli się mylę, lub sugerujcie lepszy przykład)

Podsumowując, możesz odpowiedzieć tylko na pytanie „czy te dwa ciągi są takie same, ale w różnych przypadkach”, jeśli wiesz, w jakim języku jest ten tekst . Jeśli nie wiesz, musisz wziąć łódkę. Biorąc pod uwagę hegemonię oprogramowania w języku angielskim, prawdopodobnie powinieneś uciekać się CultureInfo.InvariantCulture, ponieważ będzie to źle znane.


67
Dlaczego nie culture.CompareInfo.IndexOf(paragraph, word, CompareOptions.IgnoreCase) >= 0? Wykorzystuje właściwą kulturę i nie rozróżnia wielkości liter, nie przydziela tymczasowych ciągów małych liter i pozwala uniknąć pytania, czy konwersja na małe litery i porównywanie jest zawsze takie samo jak porównanie bez rozróżniania wielkości liter.
kwatermistrz

9
To rozwiązanie również niepotrzebnie zanieczyszcza stertę, przydzielając pamięć na funkcję, która powinna być funkcją wyszukiwania
JaredPar

15
Porównanie z ToLower () da różne wyniki z indeksu IndexOf bez rozróżniania wielkości liter, gdy dwie różne litery mają tę samą małą literę. Na przykład wywołanie ToLower () na U + 0398 „Grecka Wielka Litera Theta” lub U + 03F4 „Grecka Wielka Litera Theta Symbol” powoduje U + 03B8, „Grecką Mała Litera Theta”, ale wielkie litery są uważane za różne. Oba rozwiązania uważają różne małe litery z tą samą wielką literą, takie jak U + 0073 „Latin Small Letter S” i U + 017F „Latin Small Letter Long S”, więc rozwiązanie IndexOf wydaje się bardziej spójne.
kwatermistrz

3
@Quartermeister - i BTW, uważam, że .NET 2 i .NET4 zachowują się inaczej w tym przypadku, ponieważ .NET 4 zawsze używa NORM_LINGUISTIC_CASING, podczas gdy .NET 2 nie (te flagi pojawiły się w systemie Windows Vista).
Simon Mourier

10
Dlaczego nie napisałeś „ddddfg” .IndexOf („Df”, StringComparison.OrdinalIgnoreCase)?
Chen

2712

Możesz użyć metody String.IndexOf i podaćStringComparison.OrdinalIgnoreCase jako typ wyszukiwania do użycia:

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

Jeszcze lepsze jest zdefiniowanie nowej metody rozszerzenia ciągu:

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

Uwaga: propagacja zerowa ?. jest dostępna od wersji C # 6.0 (VS 2015) w przypadku starszych wersji

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

STOSOWANIE:

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

3
Świetna metoda przedłużania łańcucha! Zedytowałem mój, aby sprawdzić, czy łańcuch źródłowy nie ma wartości zerowej, aby zapobiec wystąpieniu błędów odwołania do obiektu podczas wykonywania funkcji .IndexOf ().
Richard Pursehouse

8
To daje taką samą odpowiedź jak w paragraph.ToLower(culture).Contains(word.ToLower(culture))przypadku CultureInfo.InvariantCulturei nie rozwiązuje żadnych problemów z lokalizacją. Dlaczego zbyt skomplikować? stackoverflow.com/a/15464440/284795
Pułkownik Panic

60
@ ColonelPanic ToLowerwersja zawiera 2 przydziały, które nie są potrzebne w operacji porównywania / wyszukiwania. Po co niepotrzebnie alokować w scenariuszu, który tego nie wymaga?
JaredPar

4
@Seabiscuit że nie będzie działać, ponieważ stringto IEnumerable<char>stąd, że nie można go używać do znalezienia podciągi
JaredPar

6
Słowo ostrzeżenia: Domyślnym ustawieniem dla string.IndexOf(string)jest użycie bieżącej kultury, podczas gdy domyślnym dla string.Contains(string)jest użycie porządkowego porównania. Jak wiemy, ten pierwszy można zmienić, wybierając dłuższe przeciążenie, a drugiego nie można zmienić. Konsekwencją tej niespójności jest następujący przykładowy kod:Thread.CurrentThread.CurrentCulture = CultureInfo.InvariantCulture; string self = "Waldstrasse"; string value = "straße"; Console.WriteLine(self.Contains(value));/* False */ Console.WriteLine(self.IndexOf(value) >= 0);/* True */
Jeppe Stig Nielsen

231

Możesz użyć w IndexOf()ten sposób:

string title = "STRING";

if (title.IndexOf("string", 0, StringComparison.CurrentCultureIgnoreCase) != -1)
{
    // The string exists in the original
}

Ponieważ 0 (zero) może być indeksem, sprawdzasz względem -1.

MSDN

Liczona od zera pozycja indeksu wartości, jeśli ten ciąg zostanie znaleziony, lub -1, jeśli nie jest. Jeśli wartością jest String.Empty, zwracana jest wartość 0.


148

Alternatywne rozwiązanie przy użyciu Regex:

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

6
Dobry pomysł, mamy też wiele bitowych kombinacji w RegexOptions, takich jak RegexOptions.IgnoreCase & RegexOptions.IgnorePatternWhitespace & RegexOptions.CultureInvariant;dla każdego, jeśli pomoże.
Saravanan

7
Muszę powiedzieć, że wolę tę metodę, chociaż używam IsMatch do schludności.
wonea

31
Co gorsza, ponieważ ciąg wyszukiwania jest interpretowany jako wyrażenie regularne, wiele znaków interpunkcyjnych spowoduje niepoprawne wyniki (lub wyzwolenie wyjątku z powodu nieprawidłowego wyrażenia). Spróbuj wyszukać "."w "This is a sample string that doesn't contain the search string". Lub spróbuj wyszukać "(invalid", jeśli o to chodzi.
cHao

17
@cHao: W takim przypadku Regex.Escapemoże pomóc. Regex nadal wydaje się niepotrzebny, gdy IndexOf/ rozszerzenie Containsjest proste (i prawdopodobnie bardziej przejrzyste).
Dan Mangiarelli,

6
Zauważ, że nie sugerowałem, że to rozwiązanie Regex było najlepszym rozwiązaniem. Po prostu dodawałem do listy odpowiedzi na pierwotnie opublikowane pytanie „Czy istnieje sposób, aby następujący zwrot był prawdziwy?”.
Jed

79

Zawsze możesz najpierw po prostu zwiększyć lub zmniejszyć ciągi znaków.

string title = "string":
title.ToUpper().Contains("STRING")  // returns true

Ups, właśnie widziałem ten ostatni kawałek. Porównywanie bez rozróżniania wielkości liter *prawdopodobnie *i tak zrobiłoby to samo, a jeśli wydajność nie stanowi problemu, nie widzę problemu z tworzeniem dużych liter i porównywaniem ich. Mógłbym przysiąc, że raz widziałem porównanie bez rozróżniania wielkości liter ...


122
Wyszukaj „Test Turcji” :)
Jon Skeet

7
W niektórych francuskich lokalizacjach wielkie litery nie mają znaków diakrytycznych, więc ToUpper () może nie być lepszy niż ToLower (). Powiedziałbym, że używaj odpowiednich narzędzi, jeśli są dostępne - porównanie bez rozróżniania wielkości liter.
Blair Conrad

5
Nie używaj ToUpper ani ToLower i rób to, co powiedział Jon Skeet
Peter Gfader

14
Widziałem to ponownie po dwóch latach i nowej opinii… w każdym razie zgadzam się, że istnieją lepsze sposoby porównywania ciągów. Jednak nie wszystkie programy będą lokalizowane (większość tego nie zrobi), a wiele z nich to aplikacje wewnętrzne lub jednorazowe. Ponieważ nie mogę się spodziewać kredytu za porady, które najlepiej zostawić na wyrzucane aplikacje ... Idę dalej: D
Ed S.

8
Czy wyszukiwanie „testu Turcji” jest tym samym, co wyszukiwanie „TESTU TURCJI”?
JackAce

55

Tylko .NET Core 2.0+ (od teraz)

.NET Core ma kilka metod radzenia sobie z tym od wersji 2.0:

  • String.Contains (Char, StringComparison )
  • String.Contains (String, StringComparison )

Przykład:

"Test".Contains("test", System.StringComparison.CurrentCultureIgnoreCase);

Z czasem prawdopodobnie wejdą do standardu .NET, a stamtąd do wszystkich innych implementacji biblioteki klas podstawowych.


1
Teraz dostępny również w .NET Standard 2.1
Paweł Bulwan

52

Jednym z problemów z odpowiedzią jest to, że zgłasza wyjątek, jeśli ciąg znaków ma wartość NULL. Możesz dodać to jako czek, aby nie:

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

8
Jeśli toCheck jest pustym ciągiem, musi zwrócić true zgodnie z dokumentacją Contains: „true, jeśli parametr value występuje w tym ciągu lub jeśli wartość jest pustym ciągiem („ ”); w przeciwnym razie false”).
amurra

3
Czy na podstawie powyższego komentarza Amurry sugerowany kod nie wymaga korekty? I czy nie należy tego dodawać do zaakceptowanej odpowiedzi, aby najlepsza odpowiedź była pierwsza?
David White

13
Teraz zwróci to wartość true, jeśli source jest pustym łańcuchem lub zerowym, bez względu na to, co toCheck. To nie może być poprawne. Również IndexOf już zwraca wartość true, jeśli toCheck jest pustym łańcuchem, a źródło nie ma wartości null. Potrzebne jest tutaj sprawdzenie wartości null. Sugeruję, czy (source == null || value == null) zwraca false;
Colin

2
Źródło nie może być zerowe
Lucas

1
if (string.IsNullOrEmpty(source)) return string.IsNullOrEmpty(toCheck);
Kyle Delaney

35

Klasa StringExtension jest krokiem naprzód, połączyłem kilka powyższych postów, aby dać pełny przykład kodu:

public static class StringExtensions
{
    /// <summary>
    /// Allows case insensitive checks
    /// </summary>
    public static bool Contains(this string source, string toCheck, StringComparison comp)
    {
        return source.IndexOf(toCheck, comp) >= 0;
    }
}

dlaczego pozwalasz na KOLEJNĄ warstwę abstrakcji StringComparison?
l - '' '' ----------- '' '' '' '20

35

To jest czyste i proste.

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

31
Będzie to jednak pasować do wzoru. W przykładzie, jeśli fileNamestrma jakieś znaki specjalne regex (np *, +, ., itd.), To będzie na dłuższą zaskoczenia. Jedynym sposobem, aby to rozwiązanie działało jak właściwa Containsfunkcja, jest ucieczka fileNamestrprzez działanie Regex.Escape(fileNamestr).
XåpplI'-I0llwlg'I -

poza tym parsowanie i dopasowanie wyrażenia regularnego wymaga dużo więcej zasobów niż proste porównanie bez rozróżniania wielkości liter
phuclv

29

OrdinalIgnoreCase, CurrentCultureIgnoreCase czy InvariantCultureIgnoreCase?

Ponieważ tego brakuje, oto kilka zaleceń dotyczących tego, kiedy użyć którego:

Dos

  • Użyj StringComparison.OrdinalIgnoreCasedo porównań jako bezpiecznego domyślnego dopasowania łańcucha bez względu na kulturę.
  • Użyj StringComparison.OrdinalIgnoreCaseporównań dla zwiększenia prędkości.
  • Użyj StringComparison.CurrentCulture-basedoperacji na łańcuchach podczas wyświetlania danych wyjściowych użytkownikowi.
  • Przełącz bieżące użycie operacji na łańcuchach opartych na niezmiennej kulturze, aby zastosować niejęzykowe StringComparison.Ordinallub StringComparison.OrdinalIgnoreCasegdy porównanie jest
    nieistotne językowo (na przykład symboliczne).
  • Użyj ToUpperInvariantraczej niż ToLowerInvariantpodczas normalizacji ciągów w celu porównania.

Nie

  • Przeciążeń należy używać do operacji na łańcuchach, które nie określają jawnie ani niejawnie mechanizmu porównywania łańcuchów.
  • W większości przypadków używaj operacji StringComparison.InvariantCulturełańcuchowych opartych na łańcuchach
    ; jednym z nielicznych wyjątków byłyby
    utrwalone pod względem językowym, ale kulturowo agnostyczne dane.

W oparciu o te zasady powinieneś używać:

string title = "STRING";
if (title.IndexOf("string", 0, StringComparison.[YourDecision]) != -1)
{
    // The string exists in the original
}

mając na uwadze, że [TwojaDecyzja] zależy od powyższych zaleceń.

link do źródła: http://msdn.microsoft.com/en-us/library/ms973919.aspx


co jeśli wiesz, że zawsze dostaniesz angielski ciąg. którego użyć?
BKSpurgeon

1
@BKSpurgeon Użyłbym OrdinalIgnoreCase, jeśli sprawa nie ma znaczenia
Fabian Bigler

20

To są najprostsze rozwiązania.

  1. Według indeksu

    string title = "STRING";
    
    if (title.IndexOf("string", 0, StringComparison.CurrentCultureIgnoreCase) != -1)
    {
        // contains 
    }
  2. Zmieniając obudowę

    string title = "STRING";
    
    bool contains = title.ToLower().Contains("string")
  3. Według Regex

    Regex.IsMatch(title, "string", RegexOptions.IgnoreCase);

11

Wiem, że to nie jest C #, ale w ramach (VB.NET) jest już taka funkcja

Dim str As String = "UPPERlower"
Dim b As Boolean = InStr(str, "UpperLower")

Wariant C #:

string myString = "Hello World";
bool contains = Microsoft.VisualBasic.Strings.InStr(myString, "world");

11

InStrMetoda z VisualBasic montaż jest najlepszy, jeśli masz obawy dotyczące internacjonalizacji (lub można go reimplement). Patrząc w nim dotNeetPeek pokazuje, że uwzględnia nie tylko wielkie i małe litery, ale także typ kana i znaki o pełnej szerokości i połowie szerokości (dotyczy to głównie języków azjatyckich, chociaż istnieją również wersje alfabetu rzymskiego o pełnej szerokości) ). Pomijam niektóre szczegóły, ale sprawdź metodę prywatną 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);
}


8

Użyj tego:

string.Compare("string", "STRING", new System.Globalization.CultureInfo("en-US"), System.Globalization.CompareOptions.IgnoreCase);

26
Pytający szuka Containsnie Compare.
DuckMaestro

@DuckMaestro, zaakceptowana odpowiedź jest wdrażana za Containspomocą IndexOf. To podejście jest równie pomocne! Przykład kodu C # na tej stronie używa string.Compare (). Wybór zespołu SharePoint, który jest!
vulcan raven

6

Jest to dość podobne do innego przykładu tutaj, ale zdecydowałem się uprościć wyliczanie bool, pierwotne, ponieważ inne alternatywy zwykle nie są potrzebne. Oto mój przykład:

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

A użycie jest coś takiego:

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

6

Korzystanie z RegEx jest prostym sposobem na to:

Regex.IsMatch(title, "string", RegexOptions.IgnoreCase);

4
Twoja odpowiedź jest dokładnie taka sama jak odpowiedź guptat59, ale, jak wskazano w jego odpowiedzi, będzie pasować do wyrażenia regularnego, więc jeśli testowany ciąg znaków zawiera jakieś specjalne znaki wyrażenia regularnego, nie da pożądanego wyniku.
Casey,

2
To jest prosta kopia tej odpowiedzi i cierpi z powodu tych samych problemów, co zaznaczono w tej odpowiedzi
Liam,

Zgoda. Studiuj wyrażenia regularne
Jared

5

Aby skorzystać z odpowiedzi tutaj, możesz utworzyć metodę rozszerzenia ciągu znaków, aby uczynić ją nieco bardziej przyjazną dla użytkownika:

    public static bool ContainsIgnoreCase(this string paragraph, string word)
    {
        return CultureInfo.CurrentCulture.CompareInfo.IndexOf(paragraph, word, CompareOptions.IgnoreCase) >= 0;
    }

1
Zakładając, że akapit i słowo będą zawsze w języku amerykańskim
Boris Callens

3
Aby uniknąć problemów z zmuszaniem kultury do en-US, użyj return CultureInfo.CurrentCulture.CompareInfo.IndexOf(paragraph, word, CompareOptions.IgnoreCase) >= 0;zamiast tego.
AndrewWhalan,

3

jeśli chcesz sprawdzić, czy przekazany ciąg jest ciągiem, istnieje prosta metoda.

string yourStringForCheck= "abc";
string stringInWhichWeCheck= "Test abc abc";

bool isContained = stringInWhichWeCheck.ToLower().IndexOf(yourStringForCheck.ToLower()) > -1;

Ta wartość logiczna zostanie zwrócona, jeśli ciąg znaków jest zawarty, czy nie



2
if ("strcmpstring1".IndexOf(Convert.ToString("strcmpstring2"), StringComparison.CurrentCultureIgnoreCase) >= 0){return true;}else{return false;}

2

Możesz użyć string.indexof ()funkcji. Będzie to bez rozróżniania wielkości liter


2

Sztuczka polega na tym, aby wyszukać ciąg znaków, ignorując wielkość liter, ale zachować dokładnie to samo (z tą samą literą).

 var s="Factory Reset";
 var txt="reset";
 int first = s.IndexOf(txt, StringComparison.InvariantCultureIgnoreCase) + txt.Length;
 var subString = s.Substring(first - txt.Length, txt.Length);

Wyjście to „Reset”


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

-4

Prosty sposób dla początkujących:

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

Głosuj za to, że jesteś niepoprawny. Co jeśli tytuł = StRiNg? StRiNg! = String i StRiNg! = STRING
berniefitz

Myliłem się. Edytuj odpowiedź w następujący sposób, zbyt prosta prosta: <br/> title.ToLower (). Zawiera („ciąg”) // oczywiście „ciąg” jest małymi literami
O Thạnh Ldt
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.