Pytanie Rozróżnianie wielkości liter "Contains (string)"


Czy istnieje sposób, aby następujące zwroty były prawdziwe?

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

Wydaje się, że nie ma przeciążenia, które pozwala mi ustawić wrażliwość na wielkość liter. Obecnie JEST WIĘKSZA OBRAZ, ale to po prostu głupie (przez co mam na myśli i18n problemy związane z obudową up-and down).

AKTUALIZACJA
To pytanie jest starożytne i od tego czasu zdałem sobie sprawę, że poprosiłem o prostą odpowiedź na naprawdę rozległy i trudny temat, jeśli chcesz go dokładnie zbadać.
W większości przypadków w jednojęzycznych angielskich bazach kodu to odpowiedź będzie wystarczająca. Podejrzewam, że większość osób przybywających tutaj zalicza się do tej kategorii, jest to najpopularniejsza odpowiedź.
To Odpowiedź jednak ujawnia nieodłączny problem, że nie możemy porównywać niedopuszczalności tekstu, dopóki nie dowiemy się, że oba teksty są tą samą kulturą i wiemy, czym jest ta kultura. Jest to może mniej popularna odpowiedź, ale uważam, że jest bardziej poprawna i dlatego ją oznaczyłem.


2420
2018-01-14 21:39


pochodzenie


Jak to jest głupie? Czy masz na myśli, że robisz 2 przebiegi na strunie? Sądzę, że porównywanie bez względu na wielkość jedynie łączy dwa etapy. - Calyth
Ponieważ użyję go na worldwebz, muszę wziąć pod uwagę znaki obce. Jak wspomniano w poniższej odpowiedzi, zwiększenie i obniżenie daje problemy z internacjonalizacją. - Boris Callens
Górna obudowa obu ciągów jest głupia, ponieważ tworzysz dwa nowe ciągi, a następnie wciąż rozróżniasz wielkość liter. Istnieje niepotrzebne dodatkowe przetwarzanie i pamięć związana z tworzeniem nowych łańcuchów, takich jak ten, zwłaszcza jeśli przeszukujesz zestaw ciągów znaków, a wielkie litery wyszukiwania lub źródła są nadmiarowe. Metoda IndexOf, która pozwala na określenie wartości StringComparison jest lepsza. - Triynko
xkcd.com/979 - Francisco
@ColonelPanic: Prawidłowo. Jeśli znasz kulturę, staje się to mniejszym problemem. Ale często albo nie wiesz, albo nie obchodzi cię to. - Boris Callens


Odpowiedzi:


Aby sprawdzić, czy ciąg znaków paragraph zawiera ciąg word (dzięki @QuarterMeister)

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

Gdzie culture jest instancją CultureInfo opisujący język, w którym napisany jest tekst.

To rozwiązanie jest przejrzyste definicja niewrażliwości na wielkość liter, która jest zależna od języka. Na przykład język angielski używa znaków I i i dla wielkich i małych liter dziewiątej litery, podczas gdy język turecki używa tych znaków dla listy jedenaste i dwunaste 29-literowego alfabetu. Turecka wersja "ja" dla dużych liter to nieznany znak "İ".

Tak więc struny tin i TIN są tym samym słowem po angielsku, ale różne słowa w tureckim. Jak rozumiem, jeden oznacza "ducha", a drugi to słowo onomatopei. (Turcy, poprawcie mnie, jeśli się mylę, lub zasugeruję lepszy przykład)

Podsumowując, można 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 tekst. Jeśli nie wiesz, będziesz musiał wziąć punt. Biorąc pod uwagę angielską hegemonię w oprogramowaniu, powinieneś się tego spodziewać CultureInfo.InvariantCulture, ponieważ będzie źle w znanych sposobach.


1088
2018-03-17 18:22



Dlaczego nie culture.CompareInfo.IndexOf(paragraph, word, CompareOptions.IgnoreCase) >= 0? Wykorzystuje odpowiednią kulturę i nie rozróżnia wielkości liter, nie przydziela tymczasowych małych liter i unika pytania o to, czy konwersja na małe litery i porównywanie jest zawsze taka sama jak porównywanie bez rozróżniania wielkości liter. - Quartermeister
To rozwiązanie niepotrzebnie zanieczyszcza stertę, alokując pamięć na coś, co powinno być funkcją wyszukiwania - JaredPar
Porównywanie z ToLower () da różne wyniki z niewrażliwego na wielkość liter IndexOf, gdy dwie różne litery mają tę samą małą literę. Na przykład, wywołanie ToLower () na U + 0398 "Grecka Wielka Teta" lub U + 03F4 "Grecka Wielka Litera Theta Symbol" daje w wyniku U + 03B8, "Grecka Mała Litera T", ale wielkie litery są uważane za różne. Oba rozwiązania uwzględniają małe litery z tą samą wielką literą, takie jak U + 0073 "Łacińska mała litera S" i U + 017F "Mała litera L długa S", więc rozwiązanie IndexOf wydaje się bardziej spójne. - Quartermeister
+1 za kompletność - odpowiedzi z właściwą formą wyjaśnienia są jedynym sposobem, w jaki użytkownicy będą się uczyć od SO - TheGeekZn
Dlaczego nie napisałeś "ddddfg" .IndexOf ("Df", StringComparison.OrdinalIgnoreCase)? - Chen


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

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

Jeszcze lepiej jest zdefiniować nową metodę rozszerzenia dla ciągu znaków:

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

Zauważ, że propagacja zerowa  ?. jest dostępny od C # 6.0 (VS 2015), do używania starszych wersji

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

STOSOWANIE:

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

2361
2018-01-14 21:44



Doskonała metoda przedłużania ciągu! Edytowałem moje, aby sprawdzić, czy łańcuch źródłowy nie ma wartości NULL, aby zapobiec występowaniu błędów odniesień do obiektów podczas wykonywania .IndexOf (). - Richard Pursehouse
To daje tę samą odpowiedź, co paragraph.ToLower(culture).Contains(word.ToLower(culture)) z CultureInfo.InvariantCulture i nie rozwiązuje problemów związanych z lokalizacją. Po co komplikować rzeczy? stackoverflow.com/a/15464440/284795 - Colonel Panic
@ColonelPanic the ToLower wersja zawiera 2 alokacje, które są niepotrzebne w operacji porównania / wyszukiwania. Po co niepotrzebnie przydzielać scenariusz, który tego nie wymaga? - JaredPar
@Seabiscuit, który nie zadziała, ponieważ string to jest IEnumerable<char> dlatego nie można go użyć do znalezienia podciągów - JaredPar
Słowo ostrzeżenia: domyślne dla string.IndexOf(string) jest użycie obecnej kultury, podczas gdy domyślne dla string.Contains(string) jest użycie porównania porządkowego. Jak wiemy, ten pierwszy można zmienić wybierając dłuższe przeciążenie, podczas gdy drugi nie może zostać zmieniony. Konsekwencją tej niespójności jest poniższy przykład kodu: 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


Możesz użyć IndexOf() lubię to:

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

MSDN

Pozycja indeksu bazująca na zera, jeśli ten łańcuch zostanie znaleziony lub -1   Jeżeli nie jest. Jeśli wartością jest String.Empty, zwracana jest wartość 0.


203
2018-01-14 21:48





Alternatywne rozwiązanie przy użyciu Regex:

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

Ogłoszenie

Jak zaznaczył @cHao w swoim komentarzu, istnieją scenariusze, które spowodują, że to rozwiązanie zwróci nieprawidłowe wyniki. Upewnij się, że wiesz, co robisz, zanim wdrożysz to rozwiązanie przypadkowo.


116
2017-07-28 17:18



Dobry pomysł, mamy również wiele kombinacji bitowych w RegexOptions jak RegexOptions.IgnoreCase & RegexOptions.IgnorePatternWhitespace & RegexOptions.CultureInvariant; dla każdego, jeśli pomaga. - Saravanan
Muszę powiedzieć, że wolę tę metodę, chociaż używam IsMatch do schludności. - wonea
Co gorsza, ponieważ łańcuch wyszukiwania jest interpretowany jako wyrażenie regularne, liczba 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", z tego powodu. - cHao
@ cHao: W takim przypadku Regex.Escape może pomóc. Regex wciąż wydaje się niepotrzebny, kiedy IndexOf / rozbudowa Contains jest prosty (i prawdopodobnie bardziej przejrzysty). - Dan Mangiarelli
Zauważ, że nie sugerowałem, że to rozwiązanie Regex jest najlepszym rozwiązaniem. Dodałem po prostu listę odpowiedzi do pierwotnie wysłanego pytania: "Czy istnieje sposób, aby następujące zwroty były prawdziwe?". - Jed


Zawsze możesz najpierw podnieść lub obniżyć strunę.

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

Ups, właśnie to zobaczyłem. Porównanie niewrażliwe na wielkość liter *prawdopodobnie* mimo to wykonaj to samo, a jeśli wydajność nie jest problemem, nie widzę problemu z tworzeniem wielkich liter i ich porównywaniem. Mógłbym przysiąc, że kiedyś widziałem, jak nieczułe na wielkość porównań raz ...


63
2018-01-14 21:42



Co ciekawe, widziałem, jak ToUpper () zalecał używanie ToLower () w tego rodzaju scenariuszu, ponieważ najwyraźniej ToLower () może "stracić wierność" w pewnych kulturach - to jest, dwie różne wielkie litery tłumaczą się na to samo mała litera. - Matt Hamilton
Szukaj "testu z Turcji" :) - Jon Skeet
W niektórych francuskich ustawieniach regionalnych wielkie litery nie zawierają znaków diakrytycznych, więc funkcja ToUpper () może nie być lepsza od ToLower (). Powiedziałbym, że użyj odpowiednich narzędzi, jeśli są one dostępne - porównuj wielkość liter. - Blair Conrad
Nie używaj ToUpper lub ToLower i rób to, co powiedział Jon Skeet - Peter Gfader
Właśnie zobaczyłem to ponownie po dwóch latach i nowym przeglądzie ... W każdym razie, zgadzam się, że istnieją lepsze sposoby porównywania ciągów. Jednak nie wszystkie programy będą zlokalizowane (większość nie będzie), a wiele z nich to aplikacje wewnętrzne lub jednorazowe. Ponieważ nie mogę oczekiwać kredytu za radę, którą najlepiej zostawić w aplikacjach jednorazowych ... Idę dalej: D - Ed S.


Jednym problemem z odpowiedzią jest to, że wyrzuci wyjątek, jeśli ciąg znaków jest pusty. Możesz to dodać 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;
} 

48
2017-12-07 21:11



Jeśli toCheck jest pustym łańcuchem, który musi zwracać wartość true dla dokumentacji Contains: "true, jeśli parametr value występuje w tym ciągu lub jeśli wartość jest pustym ciągiem znaków (" "), w przeciwnym razie false." - amurra
Na podstawie powyższego komentarza Amurry, czy sugerowany kod nie musi zostać poprawiony? I czy nie powinno to być dodane do zaakceptowanej odpowiedzi, tak aby najlepsza odpowiedź była pierwsza? - David White
Teraz to zwróci wartość true, jeśli źródło jest pustym łańcuchem lub wartością pustą, bez względu na to, co należy sprawdzić. To nie może być poprawne. Również IndexOf już zwraca wartość true, jeśli to Check jest pustym łańcuchem, a źródło nie jest puste. Potrzebne jest tutaj sprawdzenie wartości zerowej. Proponuję, jeśli (source == null || value == null) zwraca false; - Colin
Źródło nie może mieć wartości null - Lucas
if (string.IsNullOrEmpty(source)) return string.IsNullOrEmpty(toCheck); - Kyle Delaney


Klasa StringExtension to droga naprzód, połączyłem kilka wpisów powyżej, aby podać 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;
    }
}

32
2017-11-18 16:48





To jest czyste i proste.

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

31
2017-11-09 04:25



To jednak będzie pasować do wzorca. W twoim przykładzie, jeśli fileNamestr ma dowolne specjalne znaki regularne (np. *, +, .itp.), wtedy będziesz zaskoczony. Jedyny sposób, aby to rozwiązanie działało jak właściwe Contains funkcja polega na ucieczce fileNamestr wykonując Regex.Escape(fileNamestr). - XåpplI'-I0llwlg'I -


OrdinalIgnoreCase, CurrentCultureIgnoreCase lub InvariantCultureIgnoreCase?

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

Dos

  • Posługiwać się StringComparison.OrdinalIgnoreCase do porównań jako bezpieczna wartość domyślna dla dopasowywania ciągów kulturowo-agnostycznych.
  • Posługiwać się StringComparison.OrdinalIgnoreCase porównania dla większej prędkości.
  • Posługiwać się StringComparison.CurrentCulture-based operacje łańcuchowe podczas wyświetlania danych wyjściowych użytkownikowi.
  • Zmień bieżące użycie operacji na ciągach na podstawie niezmiennika kultury, aby używać języka nie-językowego StringComparison.Ordinal lub StringComparison.OrdinalIgnoreCase kiedy to porównanie
    językowo nieistotne (na przykład symboliczne).
  • Posługiwać się ToUpperInvariant zamiast ToLowerInvariant gdy normalizowanie ciągów w celu porównania.

Nie zezwalaj

  • Użyj przeciążenia dla operacji na łańcuchach, które nie jawnie lub niejawnie określić mechanizm porównywania ciągów.
  • Posługiwać się StringComparison.InvariantCulture oparty łańcuch
    operacje w większości przypadków; jednym z nielicznych wyjątków byłby
    trwałe dane językowe, ale kulturowo agnostyczne.

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

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

mając na uwadze, że [YourDecision] zależy od zaleceń z góry.

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


24
2018-06-17 10:31



co jeśli wiesz, że zawsze otrzymasz angielski ciąg znaków. którego użyć? - BKSpurgeon
@ BKSpurgeon Użyłbym OrdinalIgnoreCase, jeśli sprawa nie ma znaczenia - Fabian Bigler


Takie jak to:

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

12
2017-07-13 09:54



Nie jest to związane z kulturą i w niektórych przypadkach może się nie udać. culture.CompareInfo.IndexOf (paragraph, word, CompareOptions.IgnoreCase) powinien być użyty. - hikalkan
Po co unikać string.ToLower () podczas porównywania łańcuchów niewrażliwych na wielkość liter? Tl; Dr To kosztowne, ponieważ nowy ciąg jest "produkowany". - Liam


Wiem, że to nie jest C #, ale w strukturze (VB.NET) istnieje 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");

10
2017-09-09 13:23



Czy wiesz również, jak to działa wewnętrznie? - Boris Callens