Без учета регистра «Содержит (строка)»

2911

Есть ли способ сделать следующее возвращение верным?

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

Кажется, что нет перегрузки, которая позволяет мне устанавливать чувствительность к регистру. В настоящее время я ОБОСНОВАЛ их обоих, но это просто глупо (я имею в виду проблемы с i18n, которые идут с корпусом вверх и вниз).

ОБНОВЛЕНИЕ
Этот вопрос является древним, и с тех пор я понял, что попросил простой ответ для действительно обширной и сложной темы, если вы хотите исследовать его полностью.
В большинстве случаев, в одноязычных, английских кодовых базах этого ответа будет достаточно. Я подозреваю, потому что большинство людей, приходящих сюда, попадают в эту категорию, это самый популярный ответ.
Этот ответ, однако, поднимает внутреннюю проблему, заключающуюся в том, что мы не можем сравнивать текст без учета регистра, пока мы не знаем, что оба текста - это одна и та же культура, и мы не знаем, что это за культура. Возможно, это менее популярный ответ, но я думаю, что он более правильный, и поэтому я отметил его как таковой.

Борис Калленс
источник

Ответы:

1398

Чтобы проверить, paragraphсодержит ли строка строку word(спасибо @QuarterMeister)

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

Где cultureэто пример CultureInfoописания языка, на котором написан текст.

Это решение прозрачно в отношении определения чувствительности к регистру, которое зависит от языка . Например, английский язык использует символы Iи iдля прописных и строчных букв девятой буквы, тогда как турецкий язык использует эти символы для одиннадцатой и двенадцатой букв своего алфавита длиной 29 букв. Турецкая версия «i» в верхнем регистре - это незнакомый символ «İ».

Таким образом, строки tinи TINэто одно и то же слово на английском языке , но разные слова на турецком языке . Как я понимаю, один означает «дух», а другой - слово звукоподражания. (Турки, поправьте меня, если я ошибаюсь, или предложите лучший пример)

Подводя итог, вы можете ответить только на вопрос «являются ли эти две строки одинаковыми, но в разных случаях», если вы знаете, на каком языке находится текст . Если вы не знаете, вам придется взять с собой пунт. Учитывая гегемонию английского языка в программном обеспечении, вам, вероятно, следует прибегнуть к нему CultureInfo.InvariantCulture, потому что это будет неправильно знакомыми способами.

Полковник паника
источник
67
Почему нет culture.CompareInfo.IndexOf(paragraph, word, CompareOptions.IgnoreCase) >= 0? Он использует правильную культуру и нечувствителен к регистру, он не выделяет временные строчные буквы, и он избегает вопроса о том, всегда ли преобразование в строчные буквы и сравнение всегда то же самое, что и сравнение без учета регистра.
Quartermeister
9
Это решение также излишне загрязняет кучу, выделяя память для того, что должно быть функцией поиска
JaredPar
15
Сравнение с ToLower () даст отличные результаты от нечувствительного к регистру IndexOf, когда две разные буквы имеют одинаковую строчную букву. Например, вызов ToLower () для U + 0398 «Греческая заглавная буква тета» или U + 03F4 «Греческая заглавная буква тета» приводит к U + 03B8, «Греческая строчная буква тета», но заглавные буквы считаются разными. Оба решения рассматривают строчные буквы с одинаковыми заглавными буквами по-разному, например, U + 0073 «Латинская строчная буква S» и U + 017F «Латинская строчная буква S», поэтому решение IndexOf выглядит более согласованным.
Квартемистер
3
@Quartermeister - и, кстати, я считаю, что .NET 2 и .NET4 ведут себя по-разному, так как .NET 4 всегда использует NORM_LINGUISTIC_CASING, а .NET 2 нет (этот флаг появился в Windows Vista).
Саймон Мурье
10
Почему вы не написали "ddddfg" .IndexOf ("Df", StringComparison.OrdinalIgnoreCase)?
Чен,
2713

Вы можете использовать метод String.IndexOf и передать StringComparison.OrdinalIgnoreCaseв качестве типа поиска:

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

Еще лучше определить новый метод расширения для строки:

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

Обратите внимание, что нулевое распространение ?. доступно начиная с C # 6.0 (VS 2015), для более старых версий

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

ПРИМЕНЕНИЕ:

string title = "STRING";
bool contains = title.Contains("string", StringComparison.OrdinalIgnoreCase);
JaredPar
источник
3
Отличный метод расширения строки! Я отредактировал мой, чтобы проверить, что исходная строка не пуста, чтобы предотвратить возникновение ошибок ссылки на объект при выполнении .IndexOf ().
Ричард Pursehouse
8
Это дает тот же ответ, что и paragraph.ToLower(culture).Contains(word.ToLower(culture))с, CultureInfo.InvariantCultureи не решает никаких проблем локализации. Зачем усложнять вещи? stackoverflow.com/a/15464440/284795
Полковник Паник
60
@ColonelPanic ToLowerверсия включает в себя 2 выделения, которые не нужны в операции сравнения / поиска. Зачем ненужно выделять в сценарии, который не требует этого?
JaredPar
4
@ Seabiscuit, который не будет работать, потому stringчто, IEnumerable<char>следовательно, вы не можете использовать его для поиска подстрок
JaredPar
6
Предупреждение: по умолчанию string.IndexOf(string)используется текущая культура, а по умолчанию string.Contains(string)используется порядковый компаратор. Как мы знаем, первое можно изменить, выбрав более длительную перегрузку, а второе нельзя изменить. Следствием этого несоответствия является следующий пример кода: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

Вы можете использовать IndexOf()так:

string title = "STRING";

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

Поскольку 0 (ноль) может быть индексом, вы проверяете значение -1.

MSDN

Начинающаяся с нуля позиция индекса значения, если эта строка найдена, или -1, если это не так. Если значение равно String.Empty, возвращаемое значение равно 0.

mkchandler
источник
148

Альтернативное решение с использованием Regex:

bool contains = Regex.IsMatch("StRiNG to search", Regex.Escape("string"), RegexOptions.IgnoreCase);
Джед
источник
6
Хорошая идея, также у нас есть много побитовых комбинаций в RegexOptions, как RegexOptions.IgnoreCase & RegexOptions.IgnorePatternWhitespace & RegexOptions.CultureInvariant;для любого, если это поможет.
Сараванан
7
Должен сказать, что я предпочитаю этот метод, хотя и использую IsMatch для аккуратности.
Wonea
31
Что еще хуже, поскольку строка поиска интерпретируется как регулярное выражение, ряд знаков препинания приведет к неверным результатам (или вызовет исключение из-за недопустимого выражения). Попробуйте поискать "."в "This is a sample string that doesn't contain the search string". Или попробуйте поискать "(invalid", в этом отношении.
cHao
17
@cHao: в этом случае, Regex.Escapeможет помочь. Регулярное выражение все еще кажется ненужным, когда IndexOf/ extension Containsпростое (и, возможно, более понятное).
Дэн Манджарелли
6
Обратите внимание, что я не имел в виду, что это решение Regex было лучшим способом. Я просто добавил к списку ответов на исходный вопрос «Есть ли способ сделать следующее возвращение верным?».
Джед
79

Вы всегда можете сначала поднять или опустить струны.

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

Ой, только что видел это последнее. Сравнение без учета регистра, *вероятно, *сделало бы то же самое в любом случае, и если производительность не является проблемой, я не вижу проблемы с созданием копий в верхнем регистре и их сравнением. Я мог бы поклясться, что однажды видел сравнение без учета регистра ...

Эд С.
источник
122
Ищите «Тест Турции» :)
Джон Скит
7
В некоторых французских локалях заглавные буквы не имеют диакритических знаков, поэтому ToUpper () может быть не лучше ToLower (). Я бы сказал, использовать надлежащие инструменты, если они доступны - сравнение без учета регистра.
Блэр Конрад
5
Не используйте ToUpper или ToLower и делайте то, что сказал Джон Скит
Питер Гфадер
14
Просто увидел это снова после двух лет и нового понижения ... во всяком случае, я согласен, что есть лучшие способы для сравнения строк. Однако не все программы будут локализованы (большинство не будет), и многие из них являются внутренними или одноразовыми приложениями. Поскольку я вряд ли могу рассчитывать на кредит за лучший совет для одноразовых приложений ... Я перехожу: D
Ed S.
8
Поиск в "Тесте Турции" - это то же самое, что и в поиске "TURKEY TEST"?
JackAce
55

Только .NET Core 2.0+ (на данный момент)

.NET Core имеет пару методов для решения этой проблемы начиная с версии 2.0:

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

Пример:

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

Со временем они, вероятно, перейдут в стандарт .NET и оттуда во все другие реализации библиотеки базовых классов.

Матье Ренда
источник
1
Теперь доступно также в .NET Standard 2.1
Павел Булван
52

Одна из проблем с ответом состоит в том, что он выдаст исключение, если строка имеет значение null. Вы можете добавить это как проверку, чтобы она не была:

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;
} 
FeiBao 飞 豹
источник
8
Если toCheck - пустая строка, она должна возвращать true в соответствии с документацией Contains: "true, если параметр value встречается в этой строке или значение является пустой строкой (" "); в противном случае - false."
Амурра
3
Основываясь на комментарии Амурры выше, нужно ли исправлять предложенный код? И не следует ли добавить это к принятому ответу, чтобы лучший ответ был первым?
Дэвид Уайт
13
Теперь это вернет true, если source является пустой строкой или null, независимо от того, что является toCheck. Это не может быть правильным. Также IndexOf уже возвращает true, если toCheck - пустая строка, а source не равен null. Здесь нужна проверка на ноль. Я предлагаю, чтобы (source == null || value == null) вернул false;
Колин
2
Источник не может быть нулевым
Лукас
1
if (string.IsNullOrEmpty(source)) return string.IsNullOrEmpty(toCheck);
Кайл Делани
35

Класс StringExtension - это путь вперед, я объединил пару постов выше, чтобы дать полный пример кода:

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;
    }
}
Эндрю
источник
почему вы позволяете ДРУГОЙ слой абстракции StringComparison?
l --''''''--------- '' '' '' '' '' ''
35

Это чисто и просто.

Regex.IsMatch(file, fileNamestr, RegexOptions.IgnoreCase)
takirala
источник
31
Это будет соответствовать шаблону, хотя. В вашем примере, если fileNamestrесть какие - либо специальные символы регулярных выражений (например *, +, .и т.д.) , то вы будете в течение довольно неожиданно. Единственный способ заставить это решение работать как правильная Containsфункция - это уйти fileNamestrот дел Regex.Escape(fileNamestr).
XåpplI'-I0llwlg'I -
кроме того, разбор и сопоставление регулярных выражений намного более ресурсоемки, чем простое сравнение без
учета
29

OrdinalIgnoreCase, CurrentCultureIgnoreCase или InvariantCultureIgnoreCase?

Поскольку этого не хватает, вот несколько рекомендаций о том, когда использовать какой из них:

ДОС

  • Используйте StringComparison.OrdinalIgnoreCaseдля сравнения в качестве безопасного значения по умолчанию для сопоставления строк, не зависящего от культуры.
  • Используйте StringComparison.OrdinalIgnoreCaseсравнения для увеличения скорости.
  • Используйте StringComparison.CurrentCulture-basedстроковые операции при отображении вывода пользователю.
  • Переключите текущее использование строковых операций на основе инвариантной культуры на использование нелингвистических StringComparison.Ordinalили StringComparison.OrdinalIgnoreCaseкогда сравнение не имеет
    лингвистического значения (например, символическое).
  • Используйте ToUpperInvariantвместо ToLowerInvariantнормализации строк для сравнения.

Этикет

  • Используйте перегрузки для строковых операций, которые не указывают явно или неявно механизм сравнения строк.
  • Используйте StringComparison.InvariantCultureстроковые
    операции на основе в большинстве случаев; одним из немногих исключений будет
    сохранение лингвистически значимых, но культурно-независимых данных.

На основании этих правил вы должны использовать:

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

тогда как [YourDecision] зависит от рекомендаций сверху.

ссылка на источник: http://msdn.microsoft.com/en-us/library/ms973919.aspx

Фабиан Биглер
источник
Что если ты знаешь, что всегда получишь английскую строку. какой использовать?
BKSpurgeon
1
@BKSpurgeon Я бы использовал OrdinalIgnoreCase, если случай не имеет значения
Фабиан Биглер
20

Это самые простые решения.

  1. По индексу

    string title = "STRING";
    
    if (title.IndexOf("string", 0, StringComparison.CurrentCultureIgnoreCase) != -1)
    {
        // contains 
    }
  2. Изменяя дело

    string title = "STRING";
    
    bool contains = title.ToLower().Contains("string")
  3. По регулярному выражению

    Regex.IsMatch(title, "string", RegexOptions.IgnoreCase);
ЛАВ ВИШВАКАРМА
источник
11

Я знаю, что это не C #, но в фреймворке (VB.NET) уже есть такая функция

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

C # вариант:

string myString = "Hello World";
bool contains = Microsoft.VisualBasic.Strings.InStr(myString, "world");
Serhio
источник
11

InStrМетод из VisualBasic сборки лучше всего, если у вас есть озабоченность по поводу интернационализации (или вы можете переопределить его). Если посмотреть на это, dotNeetPeek показывает, что он учитывает не только заглавные и строчные буквы, но также и символы типа кана и символы полной ширины и полуширины (в основном это актуально для азиатских языков, хотя существуют версии полной ширины и латинского алфавита). ). Я пропускаю некоторые детали, но проверь приватный метод 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);
}
Casey
источник
11

Именно так:

string s="AbcdEf";
if(s.ToLower().Contains("def"))
{
    Console.WriteLine("yes");
}
cdytoby
источник
3
Это не зависит от культуры и может не сработать в некоторых случаях. culture.CompareInfo.IndexOf (абзац, слово, CompareOptions.IgnoreCase).
Хикалкан
3
Зачем избегать string.ToLower () при сравнении строк без учета регистра? Tl; Dr Это дорого, потому что новая строка "изготовлена".
Лиам
8

Использовать этот:

string.Compare("string", "STRING", new System.Globalization.CultureInfo("en-US"), System.Globalization.CompareOptions.IgnoreCase);
mr.martan
источник
26
Спрашивающий ищет Containsне Compare.
DuckMaestro
@DuckMaestro, принятый ответ реализуется Containsс помощью IndexOf. Так что этот подход одинаково полезен! Пример кода C # на этой странице использует string.Compare (). Выбор команды SharePoint это!
вулкан ворон
6

Это очень похоже на другой пример здесь, но я решил упростить enum до bool, первичный, потому что другие альтернативы обычно не нужны. Вот мой пример:

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

И использование что-то вроде:

if( "main String substring".Contains("SUBSTRING", true) )
....
TarmoPikaro
источник
6

Использование RegEx - прямой способ сделать это:

Regex.IsMatch(title, "string", RegexOptions.IgnoreCase);
Stend
источник
4
Ваш ответ точно такой же, как у guptat59, но, как было указано в его ответе, он будет соответствовать регулярному выражению, поэтому, если тестируемая строка содержит какие-либо специальные символы регулярного выражения, она не даст желаемого результата.
Кейси
2
Это прямая копия этого ответа и страдает от тех же проблем, которые отмечены в этом ответе
Лиам
Согласовано. Изучите регулярные выражения
Джаред
5

Просто, чтобы развить ответ здесь, вы можете создать метод расширения строки, чтобы сделать его немного более удобным для пользователя:

    public static bool ContainsIgnoreCase(this string paragraph, string word)
    {
        return CultureInfo.CurrentCulture.CompareInfo.IndexOf(paragraph, word, CompareOptions.IgnoreCase) >= 0;
    }
Мельбурн Разработчик
источник
1
Предполагая, что ваш абзац и слово всегда будут в en-US
Борис Калленс
3
Чтобы избежать проблем с навязыванием культуры en-US, используйте return CultureInfo.CurrentCulture.CompareInfo.IndexOf(paragraph, word, CompareOptions.IgnoreCase) >= 0;вместо этого.
AndrewWhalan
3

если вы хотите проверить, находится ли переданная вами строка в строке, то для этого есть простой метод.

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

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

Это логическое значение вернется, если строка содержится или нет

шаишав шукла
источник
3

Как просто и работает

title.ToLower().Contains("String".ToLower())
Прадип Асанка
источник
2
if ("strcmpstring1".IndexOf(Convert.ToString("strcmpstring2"), StringComparison.CurrentCultureIgnoreCase) >= 0){return true;}else{return false;}
Тамилсельван К
источник
2

Вы можете использовать string.indexof ()функцию. Это будет без учета регистра

ОКАН САРИКА
источник
2

Хитрость здесь в том, чтобы искать строку, игнорируя регистр, но сохранять ее точно такой же (с тем же регистром).

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

Выход «Сброс»

Mr.B
источник
-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

Простой способ для новичка:

title.ToLower().Contains("string");//of course "string" is lowercase.
O Thạnh Ldt
источник
Даунвот за то, что просто неверен. Что если title = StRiNg? StRiNg! = Строка и StRiNg! = STRING
Бернифитц
Я был неправ. Отредактируйте ответ следующим образом, слишком просто: <br/> title.ToLower (). Contains ("string") // конечно "string" в нижнем регистре
O Thạnh Ldt